简体   繁体   中英

Django ModelForm Field Filtering with multiple model dependancies

I have Member model and Group model with Foreign key from Member

class Member(models.Model):
    member_name = models.CharField(max_length=100)
    member_dob = models.DateField()

class Group(models.Model):
    group_name = models.CharField(max_length=100)
    group_owner = models.ForeignKey(Member, related_name='owner')
    group_members = models.ManyToManyField(Member, related_name='members')

And I have created another model, Activity which will refer both Group and Member

class Activity(models.Model):
    group_id = models.ForeignKey(Group)
    topic = models.CharField(max_length=100)
    start_by = models.ForeignKey(Member, related_name='started')
    participants = models.ManyToManyField(Member, related_name='participants')

Now I have created a ModelForm for Acivity

class ActivityForm(ModelForm):
    class Meta:
        model = Activity

If i have used object of ActivityForm in template, all users will be loaded in participants 'Select' input. I wish to display only those members belonging to the group(with id= self.group_id) displayed in Select Option.

Can someone please help me in writing a queryset which I could include in init function of ActivityForm to do that?

Shouldn't Member have a foreign key to Group ? You want to have many members in a group not many groups with a single member right?

class Member(models.Model):
    name = models.CharField(max_length=100)
    dob = models.DateField()
    group = modes.ForeignKey('Group', related_name='members')

class Group(models.Model):
    name = models.CharField(max_length=100)
    owner = models.ForeignKey('Member', related_name='owned_groups')

class Activity(models.Model):
    group = models.ForeignKey('Group', related_name='activities')
    topic = models.CharField(max_length=100)
    start_by = models.ForeignKey('Member', related_name='started_activities')
    participants = models.ManyToManyField('Member', related_name='participating_activities')

And then you can do:

class ActivityForm(ModelForm):
    def __init__(self, *args, **kwargs):
        super(ActivityForm, self).__init__(*args, **kwargs)
        if 'initial' in kwargs:
            self.fields['participants'].queryset = Member.objects.filter(group=initial.group)
    class Meta:
        model = Activity

Edit


With the change to using a ManyToManyField I would recommend fixing your field names and related names too. It would be easier to make the changes early in the development process rather than when you run into a different problem that can't be fixed. If it makes it any easier to understand just think of it this way: the related name will be accessible on the model that the ForeignKey or ManyToManyField points to and will contain a queryset with 0 or more entries.

So if you have...

class Member(models.Model):
    name = models.CharField(max_length=100)
    dob = models.DateField()

class Group(models.Model):
    name = models.CharField(max_length=100)
    owner = models.ForeignKey('Member', related_name='owned_groups')
    members = models.ManyToManyField('Member', related_name='groups')

class Activity(models.Model):
    group = models.ForeignKey(Group)
    topic = models.CharField(max_length=100)
    start_by = models.ForeignKey('Member', related_name='started_activities')
    participants = models.ManyToManyField('Member', related_name='activities')

Then you can do things like...

g1 = Group.objects.create(name='Group 1', ...)
g2 = Group.objects.create(name='Group 2', ...)

member = Member.objects.get(name='John')
member.groups.all() # Get all the groups for the member
member.groups.add(g1, g2) # Add the member to 2 groups
member.activities.all() # Get all activities the member is a participant in
member.started_activities.all() # Get all activities started by the user

And your form...

class ActivityForm(ModelForm):
    ...
    def __init__(self, *args, **kwargs):
        super(ActivityForm, self).__init__(*args, **kwargs)
        if 'initial' in kwargs:
            self.fields['participants'].queryset = initial.group.members.all()

    class Meta: 
        model = Activity

I think that would work, but if not:

class ActivityForm(ModelForm):
    ...
    def __init__(self, *args, **kwargs):
        super(ActivityForm, self).__init__(*args, **kwargs)
        if 'initial' in kwargs:
            self.fields['participants'].queryset = Member.objects.filter(group__id=initial.group.id)

    class Meta: 
        model = Activity

Your ActivityForm class need to have an atribute:

class ActivityForm(ModelForm):
    participants = forms.ModelMultipleChoiceField(queryset=Member.objects.filter(members__id==self.instance.group_id))

Your __init__ function should look like this:

def __init__(self, *args, **kwargs):
    # call parent
    super(ActivityForm, self).__init__(*args, **kwargs)
    # fill initial values
    self.fields['participants'].initial = [c.pk for c in Member.objects.filter(members__id=self.instance.group_id, participants__in=self.instance)

1) You should rename "related_name" in group. The related name means how the field will be known from the other class eg. from the Member class

2) I think that "group_members" in your context should be ManyToManyField. Your class Group now allows you to store only one person to a group.

There could be some mistakes in naming because you have very confusing related names.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM