简体   繁体   中英

Filtering Django User Choice Field use for a form

I am working on a hard django project and I am stuck again. I have a field in the userprofile which is called troop:

class UserProfile(models.Model):

    scout_username = models.OneToOneField(User, on_delete=models.CASCADE)
    Group_Choice = Groups.Scout_Groups()
    troop = models.SlugField(max_length=27, choices=Group_Choice, default='None', blank=False)
    date_of_birth = models.DateField(default=date.today)


    def __str__(self):
        return '%s'% (self.scout_username)

def create_profile(sender, **kwargs):
    if kwargs['created']:
        user_profile = UserProfile.objects.create(user=kwargs['instance'])

post_save.connect(create_profile, sender=User)

Then I have a form which fills in data which is sent to my stData model. Within the form the user can choose to add details about another user. Except they can only add details to another user who has the same troop details. forms.py

from django import forms
from leaders.models import stData
from django.contrib.auth.models import User
from accounts.models import UserProfileManager, UserProfile

st_username_list=[
    (None, 'Choose a user'),
    (user1, 'user1'),
    (i, 'no progress'),
    ]

class BadgeForm(forms.ModelForm):
    def set_user(self, user):
        global st_username_list
        troop = user.userprofile.troop
        userprofile = UserProfile.objects.all()
        selected_st = userprofile.filter(troop=troop)
        for st in selected_st:
            username = str(st.st_username)
            st_username_list.append((username, username))
    st_username = forms.ChoiceField(choices=st_username_list)
    class Meta:
        model = stData
        fields = ('st_username', 'Pioneer_Badge', 'Explorer_Badge', 'Adventurer_Badge', 'Proficiency_Badge', 'Other_Badge') 

Please note In the example above I used a global variable. I understand this is far from desired. I have since removed it thanks to the explanation of the proper way to do the filter (found after the line break). I'm only keeping this for education reasons for others who may find they had similar problems.

I pass through the user within my views like this:

user = request.user
user_form_setting = BadgeForm.set_user(self, user)

models.py

from django.db import models
from django.contrib.auth.models import User

from accounts.st_badge_list import st_List

class stData(models.Model):

    Pioneer_Choices = st_List.Target_Badges()
    Blue_Choices = st_List.Target_Badges()
    Black_Choices = st_List.Target_Badges()
    Proficiency_Choices = st_List.Proficiency_Badges()
    Other_Choice = st_List.Other_Badges()

    Pioneer_Badge = models.CharField(max_length=16, choices=Pioneer_Choices, default='None', blank=True)
    Blue_Star = models.CharField(max_length=16, choices=Blue_Choices, default='None', blank=True)
    Black_Star = models.CharField(max_length=16, choices=Black_Choices, default='None', blank=True)
    Proficiency_Badge = models.CharField(max_length=22, choices=Proficiency_Choices, default='None', blank=True)
    Other_Badge = models.CharField(max_length=27, choices=Other_Choice, default='None', blank=True)


    st_username = models.ForeignKey(User, on_delete=models.CASCADE)
    date = models.DateTimeField(auto_now=True)
    print (User)

    def __str__(self):
        return '%s'% (self.st_username)

How would I go about having it so whatever user has the same troop details will appear within the st_username_list as a choice? After researching and trying things with the code, I have been getting: ValueError Cannot assign "'user1'": "stData.st_username" must be a "User" instance. I hope this is not too confusing.


Edit Ok so I have found that I can filter the options for the st_username by doing

def __init__(self, *args, **kwargs):

        super().__init__(*args, **kwargs)
        self.fields['st_username'].queryset = UserProfile.objects.filter(troop='''user's troop''')

Problem update My main issue now is that I am unable to pass through the user instance within the model. I have seen this question here . So I added this to my form's innit method:

    self.user = kwargs.pop('user')

Yet when I try and use the user by going self.user I get the an unhelpful error KeyError saying user . The shell indicated this may be due to the self.user = kwargs.pop(user) I believe this may be because I am not passing through the user. So when I call the form in my views, I tried form = BadgeForm(user=request.user) and got the same error.

my queryset looks like this now:

self.fields['scout_username'].queryset=UserProfile.objects.filter(troop=user.userprofile.troop)

Further Information: To understand the problem better, I have passed through a set variable of the troop within the queryset. So in this case

self.fields['scout_username'].queryset=UserProfile.objects.filter(troop='BC')

Although now I get Error AttributeError: 'BadgeForm' object has no attribute ' name ' The shell links this with the formset from which I use the form with. The details I'm provided is:

line 435, in formset_factory
    return type(form.__name__ + 'FormSet', (formset,), attrs)

I hope this makes more sense to you than to me! Any help would be greatly appreciated!

The final problem was within the use of the formset. According to the docs , the proper way to add the kwargs is to do such:

    BadgeFormsSet = formset_factory(BadgeForm)
    formset = BadgeFormsSet(form_kwargs={'user': request.user})

Hope this helps any one else!

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