I have an issue with a form with a Select input. This input is used to select a user in a list. The list should only contain user from the group currently used.
I found a solution, however I am not completely sure of what am I doing in the form definition (I do not fully understand how works the def __init__ part). And of course, my code is not working : I obtain the right form, with the choices I need, however if a submit data, it's not saved in the database.
I have been able to check if the form is valid (it is not), the error is the following :
Category - Select a valid choice. That choice is not one of the available choices.
(I have the same error for the user field). I can't find my way in this, so if you can help, would be very appreciated!
My models:
class Group(models.Model):
name = models.CharField(max_length=100)
def __str__(self):
return self.name
class User(AbstractUser):
groups = models.ManyToManyField(Group)
current_group = models.ForeignKey(Group, on_delete=models.SET_NULL,blank = True , null = True, related_name="current_group")
class Category(models.Model):
name = models.CharField(max_length=100)
groups = models.ManyToManyField(Group)
def __str__(self):
return self.name
class Expanses(models.Model):
date = models.DateTimeField()
amount = models.DecimalField(decimal_places=2, max_digits=12)
category = models.ForeignKey(Category, on_delete=models.CASCADE)
user = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)
group = models.ForeignKey(Group, on_delete=models.CASCADE)
comment = models.CharField(max_length=500)
def __str__(self):
return self.amount
My form:
class CreateExpanseForm(forms.ModelForm):
class Meta:
model = Expanses
fields = ['date', 'amount', 'category', 'user','comment']
widgets={'date':DateInput(attrs={'placeholder':'Date', 'class':'form-input', 'type':'date'}),
'amount':TextInput(attrs={'placeholder':'Amount', 'class':'form-input', 'type':'text'}),
'category':Select(attrs={'placeholder':'Category', 'class':'form-select', 'type':'text'}),
'user':Select(attrs={'placeholder':'user', 'class':'form-select', 'type':'text'}),
'comment':TextInput(attrs={'placeholder':'comment', 'class':'form-input', 'type':'text'}),}
def __init__(self, *args, **kwargs):
user_choices = kwargs.pop('user_choices', None)
category_choices = kwargs.pop('category_choices', None)
super().__init__(*args, **kwargs)
if user_choices:
self.fields['user'].choices = user_choices
if category_choices:
self.fields['category'].choices = category_choices
My view:
def SummaryView(request):
createExpanseForm = CreateExpanseForm(user_choices = [(user.username, user.username) for user in request.user.current_group.user_set.all()],
category_choices = [(category.name, category.name) for category in request.user.current_group.category_set.all()])
if request.method == "POST":
if 'createExpanse' in request.POST:
createExpanseForm = CreateExpanseForm(user_choices = [(user.username, user.username) for user in request.user.current_group.user_set.all()],
category_choices = [(category.name, category.name) for category in request.user.current_group.category_set.all()],
data=request.POST)
if createExpanseForm.is_valid():
expanse = createExpanseForm.save()
if expanse is not None:
expanse.group = request.user.current_group
expanse.save()
else:
messages.success(request, "Error!")
context = {'createExpanseForm':createExpanseForm}
return render(request, 'app/summary.html', context)
The form field of a
ForeignKeyis aModelChoiceField[Django-doc], this neecds the primary key of the item to work with, not the name of a category. You thus try to do too much yourself, and thus invalidated the data of the form field.I suggest that you let the form handle it and specify the queryset when necessary:
In the view you can then just pass the logged in user: