代码之家  ›  专栏  ›  技术社区  ›  Lance E.T. Compte

Django型多功能反向滤波器

  •  2
  • Lance E.T. Compte  · 技术社区  · 7 年前

    以下是我的模型摘录(类似于):

    class Person(models.Model):
      name = models.CharField(max_length=20)
      relationships = models.ManyToManyField('self',
        through='Relationship',
        symmetrical=False,
        related_name='related_to',
      )
      def __str__(self):
        return self.name
    
    class Relationship(models.Model):
      from_person = models.ForeignKey(Person,
        related_name='from_people',
        on_delete=models.CASCADE,
      )
      to_person = models.ForeignKey(Person,
        related_name='to_people',
        on_delete=models.CASCADE,
      )
      status = models.CharField(max_length=20)
      def __str__(self):
        return "{} is {} {}".format(
          self.from_person.name, self.status, self.to_person.name)
    

    以下是我的数据库内容:

    >>> Person.objects.all()
    <QuerySet [<Person: A>, <Person: B>, <Person: C>]>
    >>> Relationship.objects.all()
    <QuerySet [<Relationship: B is Following C>]>
    

    如果我想知道某个给定的人在跟踪谁,我可以在person类中构建一个新方法:

    def get_following(self):
      return self.relationships.filter(
        to_people__status='Following',
        to_people__from_person=self)
    

    这项工作:

    >>> p2.get_following()
    <QuerySet [<Person: C>]>
    

    我想做与此相反的事情。与其问“这个人跟着谁?”,我想问“谁跟踪这个人?”。我可以这样做(尽管它返回关系对象,而不是Person对象):

    >>> Relationship.objects.filter(to_person=p3, status='Following')
    <QuerySet [<Relationship: B is Following to C>]>
    

    我的尝试如下(返回一个空查询集):

    def get_following(self):
      return self.relationships.filter(
        from_people__status='Following',
        from_people__to_person=self)
    

    非常感谢您的帮助!

    编辑: 以下是我选择的答案:

    def get_followers(self):
      return self.related_to.filter(from_people__status='Following')
    
    4 回复  |  直到 7 年前
        1
  •  1
  •   ElectRocnic    6 年前

    以防其他人需要与您描述的完全相同的另一种实现“关注者”的方法,但使用不同的建模方案:

    # project/account/models.py
    from django.db import models
    from django.contrib.auth.models import AbstractBaseUser, BaseUserManager, PermissionsMixin
    from model_utils import Choices
    
    class User(AbstractBaseUser, PermissionsMixin):
        class Meta:
            # ...
    
        username = models.CharField(...)
        email = models.EmailField(...)
        # ...
    
        ### Custom app-specific relationships (database scheme) ###
    
        # symmetrical=False is needed for this reason: https://stackoverflow.com/a/42040848/3433137
        following = models.ManyToManyField('self', related_name='followers', blank=True, symmetrical=False)
    
    # project/account/forms.py
    from django import forms
    from django.contrib.auth import get_user_model
    from django.contrib.admin.widgets import FilteredSelectMultiple
    from .models import User
    
    class UserChangeForm(forms.ModelForm):
        # ...
    
        # new:
        following = forms.ModelMultipleChoiceField(
            queryset=User.objects.all(),
            required=False,
            widget=FilteredSelectMultiple(
                verbose_name='Following',
                is_stacked=False
            )
        )
        # new:
        followers = forms.ModelMultipleChoiceField(
            queryset=User.objects.all(),
            required=False,
            widget=FilteredSelectMultiple(
                verbose_name='Followers',
                is_stacked=False
            )
        )
    
        class Meta:
            model = get_user_model()
            # add 'following' and 'followers' to the fields:
            fields = ('email', 'password', ..., 'following', 'followers')
    
        # also needed to initialize properly:
        def __init__(self, *args, **kwargs):
            super(UserChangeForm, self).__init__(*args, **kwargs)
    
            # Filter out the self user in the lists and initialize followers list:
            if self.instance and self.instance.pk:
                self.fields['following'] = forms.ModelMultipleChoiceField(
                    queryset=User.objects.all().exclude(pk=self.instance.pk),
                    required=False,
                    widget=FilteredSelectMultiple(
                        verbose_name='Following',
                        is_stacked=False
                    )
                )
                self.fields['followers'] = forms.ModelMultipleChoiceField(
                    queryset=User.objects.all().exclude(pk=self.instance.pk),
                    required=False,
                    widget=FilteredSelectMultiple(
                        verbose_name='Followers',
                        is_stacked=False
                    )
                )
                self.fields['followers'].initial = self.instance.followers.all()
    
    # project/account/admin.py
    from django.contrib.auth import get_user_model
    from django.contrib import admin
    from django.contrib.auth.models import Group
    from django.contrib.auth.admin import UserAdmin as BaseUserAdmin
    from .models import User
    from .forms import UserChangeForm, UserCreationForm
    
    
    class Admin(BaseUserAdmin):
        add_form = UserCreationForm
        form = UserChangeForm
        model = get_user_model()
    
        # The fields to be used in displaying the User model.
        # These override the definitions on the base UserAdmin
        # that reference specific fields on auth.User.
        list_display = ['email', 'username', 'is_admin']
        list_filter = ('is_admin',)
        fieldsets = (
            (None, {'fields': ('email', 'password')}),
            ('Personal info', {'fields': ('username',)}),
            ('Permissions', {'fields': ('is_admin', 'is_superuser', 'is_staff')}),
            # new:
            ('Following / Followers', {'fields': ('following', 'followers')}),
        )
        # other fields
        # ...
    
        # new:
        filter_horizontal = ('following', 'followers')
    
    admin.site.register(User, Admin)
    admin.site.unregister(Group)
    

    如果然后启动服务器并转到localhost:8000/admin/ 然后导航到用户的详细信息页面,您应该会在屏幕上看到类似的内容:

    enter image description here

    我没有添加计数器,因此您可以在list\u视图中一次查看关注者的数量。

    笔记 在管理面板中,第二个包含followers的FormField是只读的。用户不能选择其他用户跟随他。

        2
  •  0
  •   Aipi    7 年前

    你有这样一个查询集: <QuerySet [<Relationship: B is Following C>]> . 想想有一天(我猜这是那个“人”的提议)有很多追随者,可能会有这么多追随者,就像这样: <QuerySet [<Relationship: B is Following C>, <Relationship: A is Following C>]> . 因此,我将使用values\u list()[1]:

    Relationship.objects.filter(to_person=p3, status='Following').values_list('from_person__name', flat=True)
    

    返回值: <QuerySet [A, B, ...]>

    如果只传入单个字段,也可以传入平面参数。如果为True,则意味着返回的结果是单个值,而不是一个元组。

    或创建方法:

    def get_followers(self):
        follower_of_person = []
        for value in relation_of_person:
            follower_of_p3.append(value.from_person.name)
        return follower_of_person
    

    返回值: [A, B, ...]

    values\u list仍然更好,因为您直接在数据库上工作。

    [1] https://docs.djangoproject.com/en/2.0/ref/models/querysets/#values-list (这里有一个很好的例子。

        3
  •  0
  •   addohm    7 年前

    看看 this 文件否则,这里有一些其他方法。。。

    self.relationships.from_people.objects.all() 将返回具有相关名称的所有对象 from_people .

    然而,我会稍微更改一些代码,以便使用 self.relationships.from_people.objects.filter(status='Following')

    另一种方法(虽然不是最有效的)是传入person模型,并使用person传入relationships模型。pk作为过滤器。

    def get_following(self, pk):
        person = Person.objects.get(pk=pk)
        relationships = Relationship.objects.filter(to_person=person.id)
        return relationships
    
        4
  •  0
  •   Lance E.T. Compte    7 年前

    以下是我选择的答案:

    def get_followers(self):
      return self.related_to.filter(from_people__status='Following')