1
votes

Relation de filtre Django dans models.py

En supposant le scénario suivant sur deux applications séparées où je souhaite compter le nombre d'éléments de publication d'une catégorie spécifique dans mon modèle:

Categories / models.py

<h5>Number of Post elements: {{ category.posts }}</h5>

Posts / models.py

class Post(models.Model):
    id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
    author = models.ForeignKey(User, on_delete=models.CASCADE)
    title = models.CharField(verbose_name="Title")
    content = models.TextField(verbose_name="Content")
    tag = models.CharField(verbose_name="Meta", max_length=70, blank=True)
    category = models.ForeignKey(Category, on_delete=models.CASCADE, null=True)

template.html:

from Posts.models import Post
...
class Category(models.Model):
    id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
    title = models.CharField(max_length=20, verbose_name="Title")
    ...

    @property
    def posts(self):
        return Post.objects.filter(category_id=self.id).count()

malheureusement, cela entraîne toujours l'erreur suivante:

ImportError: impossible d'importer le nom 'Post' depuis partiellement initialisé module 'Posts.models' (probablement en raison d'une importation circulaire)


2 commentaires

vous avez from Posts.models import Post dans votre modèle Catégories ?


Oui. Merci pour votre indice, je viens de mettre à jour ma question en conséquence


4 Réponses :


0
votes

Cela est probablement dû au fait que vous importez des Posts.models dans vos Categories.models et vice-versa . Vous pouvez l'importer dans la propriété elle-même:

# no from Posts.models import Post

# …

class Category(models.Model):
    id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
    title = models.CharField(max_length=20, verbose_name="Title")
    # …

    @property
    def posts(self):
        return self.post_set.count()

Mais vous n'avez pas besoin d'importer ce modèle. Django crée automatiquement une relation inversée. Si vous ne spécifiez pas le related_name , c'est modelname_set , vous pouvez donc y accéder avec:

# no from Posts.models import Post

# …

class Category(models.Model):
    id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
    title = models.CharField(max_length=20, verbose_name="Title")
    # …

    @property
    def posts(self):
        from Posts.models import Post
        return Post.objects.filter(category_id=self.id).count()

Remarque : les modules Python sont normalement écrits en snake_case , pas en PerlCase , donc il doit être category , pas Category .


1 commentaires

Merci pour cela :) je préfère la première solution car il est toujours possible d'interroger d'autres champs, merci



0
votes

Vous devriez essayer d'utiliser le nom associé dans votre propriété:

class Category(models.Model):
    id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
    title = models.CharField(max_length=20, verbose_name="Title")
    ...

    @property
    def posts(self):
        return self.post_set.count()

De plus, pour mémoire, il semble étrange d'avoir Category et Post dans des applications séparées. Ils devraient probablement être dans une seule application blog . Les importations circulaires impliquent généralement que vous avez deux applications trop étroitement couplées. Dans ce cas, il ne devrait s'agir que d'une seule application.


0 commentaires

0
votes

L'indice est dans l'erreur car vous avez effectivement une importation circulaire.

Posts.models.py aura de Categories.models import Category en haut car vous l'utilisez comme une clé étrangère et comme vous utilisez maintenant Post dans la méthode posts de Category, elle aura from Posys.models import Post

Vous devez résoudre ce problème, ce que vous pouvez faire en un de trois façons selon que vous avez d'autres modèles ou méthodes utilisant la classe Post à partir du modèle Catégories.

Si vous le faites, vous pouvez utiliser l'une de ces deux

  1. Changez la ForeignKey pour passer une chaîne plutôt que l'objet Category donc
    @property
    def posts(self):
        return self.post_set.count()
  1. Déplacez l'importation du haut de Categories / models.py à l'intérieur de la méthode posts afin qu'elle importe localement
class Category(models.Model):
    id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
    title = models.CharField(max_length=20, verbose_name="Title")

    @property
    def posts(self):
        from Posts.models import Post
        return Post.objects.filter(category_id=self.id).count()

Si vous ne le faites pas, et que nous ne pouvons pas dire ce que vous avez publié, vous pouvez simplement utiliser le nom associé `` post_set`` dans la catégorie classe et évitez du tout l'importation. https://docs.djangoproject.com/en/3.0/topics/db/models/#be-careful-with-related-name-and-related-query-name

class Post(models.Model):
    id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
    author = models.ForeignKey(User, on_delete=models.CASCADE)
    title = models.CharField(verbose_name="Title")
    content = models.TextField(verbose_name="Content")
    tag = models.CharField(verbose_name="Meta", max_length=70, blank=True)
    category = models.ForeignKey("Category", on_delete=models.CASCADE, null=True)

Cela résoudra également le problème


0 commentaires

0
votes

Pas besoin de cette propriété, vous pouvez accéder aux objets associés en suivant la clé étrangère vers l'arrière. Consultez la documentation

<h5>Number of Post elements: {{ category.post_count }}</h5>

Ou vous pouvez ajouter une propriété mais directement:

@property
def post_count(self):
    return self.post_set.count()

Et puis:

<h5>Number of Post elements: {{ category.post_set.count }}</h5>


0 commentaires