Lets say I have the following model
class Application(models.Model):
occupation = models.TextField()
and form
class ApplicationForm(forms.ModelForm):
def __init__(self, *args, **kwargs)
super().__init__(*args, **kwargs)
self.fields['occupation'] = forms.MultipleChoiceField(choices=OCCUPATION_CHOICES, widget=CheckboxSelectMultiple)
class Meta:
model = Application
When I use it with a instance (for example via admin) the choices are not selected.
What am I doing wrong?
Edit: Clarification: When I select some choices, I hit submit it saves the data. They look like ['undergraduate', 'postdoc'] in the database. But they are not checked in the form anymore.
I managed to get it working like this.
import ast
class ApplicationForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
if kwargs.get('instance'):
kwargs['initial'] = {
'occupation': ast.literal_eval(kwargs.get('instance').occupation or '[]'),
}
super().__init__(*args, **kwargs)
class Meta:
model = Application
widgets = {
'occupation': CheckboxSelectMultiple(choices=OCCUPATION_CHOICES),
}
Thanks go to RodrigoDela who made me realize what I was doing wrong and pointed me in the right direction.
Edit: A TextField cannot store multiple choices. Take a look at this:
Django Model MultipleChoice
Either you use this, or you override your forms, so that these can parse the instance.occupation content into their form.occupation field.
Related
I have Django Model that has a live boolean attribute.
I want the View to get the Model by slug and only go to this page if it's live USING THE DetailView (not function based view, because I want to see how to do it.
Model definition
# myapp/models.py
class MyModel(models.Model):
name = models.CharField(max_length=255)
live = models.BooleanField(default=True)
slug = models.SlugField()
def save(self, *args, **kwargs):
self.slug = slugify(self.name)
I hoped it would be done something like this:
class ModelDetailView(DetailView):
model = MyModel
def get(self, request, *args, **kwargs):
service = self.get_object_or_404(Service, live=True) # <- Main point of what I'm looking for help with
return super().get(request, *args, *kwargs)
Is there a way to filter this way?
You can specify the queryset to filter, so:
class ModelDetailView(DetailView):
model = MyModel
queryset = MyModel.objects.filter(live=True)
You thus do not need to implement the .get(…) method at all.
I have a ModelForm with the following init method:
def __init__(self, *args, **kwargs):
super(FragebogenForm, self).__init__(*args, **kwargs)
self.fields['birth_date'].widget.attrs.update({'type': 'date'})
This doesn't change the type attribute of the input tag, although it should according to the documentation (ctrl + f -> "Or if the field isn’t declared directly on the form"). If I change it to e.g. .widget.attrs.update({'placeholder': '12.12.1999'}) it works, the new placeholder appears on the page. Only setting the type to date does not work, but why?
I just discovered my own error. I didn't put the initializer or __init__ outside the class metafunction. None of my widgets worked well
###Ensure the init block is outside
class ProfileForm(ModelForm):
class Meta:
model = Profile
fields = ['avatar','company']
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.fields['avatar'].widget.attrs.update({'class': 'form-control'})
The type of the widget is determined by the .input_type attribute, so:
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.fields['birth_date'].widget.input_type = 'date'
This is however often not a good idea, since other items of the widget are then not altered correctly. Normally you specify the widget in the Meta class:
class MyForm(forms.ModelForm):
class Meta:
model = Meta
widgets = {
'birth_date': forms.DateInput()
}
But if the widget is not a DateInput, then likely the field birth_date in your model is not a models.DateField, so it might be better to fix this instead of solving it at the widget level.
I have been trying for awhile now without any luck.. I have model Like this:
class List(models.Model):
name = models.CharField(max_length=100, default="")
user = models.ForeignKey(User, on_delete=models.CASCADE, related_name='lists')
def __str__(self):
returnself.name
class Meta:
unique_together = ['name', 'user']
Every user can create their own lists and add values to those lists. I have adding values and everything else working but to the form that adds these values I would somehow need to filter to show only users own lists, now its showing all lists created by every user... this is the form:
class data_form(forms.Form):
user_lists = List.objects.all()
selection = forms.ModelChoiceField(queryset=user_lists)
data = forms.IntegerField()
Any ideas how to filter it? I have tempoary "list.objects.all()" since dont want it to give error that crashes the server. I have watched a ton of examples on stackoverflow but none of them seems to be exact thing that I am looking for.. Thanks already for asnwers! :)
You need to get hold of the current user, e.g. like so or so.
That is, you pass request.user to the form when instantiating it in your view:
frm = DataForm(user=request.user)
In the __init__ of your form class, you can then assign the user-filtered queryset to your field:
class DataForm(forms.Form):
def __init__(self, *args, **kwargs):
user = kwargs.pop("user")
super(DataForm, self).__init__(*args, **kwargs)
self.fields['selection'].queryset = List.objects.filter(user=user)
You can set your form to take the user when initialized, and from there get a new queryset filtered by user.
class DataForm(forms.Form):
selection = forms.ModelChoiceField(queryset=List.objects.none())
data = forms.IntegerField()
def __init__(self, user, *args, **kwargs):
super().__init__(*args, **kwargs)
self.fields['selection'].queryset = List.objects.filter(user=user)
You would inititialize the form like this:
form = DataForm(request.user)
I know it's there somewhere but I can't find it.
So I have a 'category' model and a 'book' model which has a many to many to 'category'. When creating a new book in a modelform, all the categories are presented to the user to assign to the book. In that case I want only the categories created by the current user to show up in that field, not all the categories.
What's the best approach?
Assuming your model like:
class Category(models.Model):
....
creator = models.ForeignKey(User)
class Book(models.Model):
...
categories = models.ManyToManyField(Category)
Assuming your form like:
class BookForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
current_user = kwargs.pop('user')
super(BookForm, self).__init__(*args, **kwargs)
self.fields['categories'].queryset = Categories.objects.filter(creator=current_user)
So, you need to overide __init__ of your form, pass the current user to this form. And then set a queryset attribute on the ManyToManyField you want.
Your view:
#GET request
book_form = BookForm(user=request.user)
#POST request
book_form = BookForm(data=request.POST, user=request.user)
Is there a more efficient, or cleaner way to do the following?
class SpamForm(ModelForm):
some_choices = fields.MultipleChoiceField()
def __init__(self, *args, **kwargs):
super(SpamForm, self).__init__(*args, **kwargs)
self.fields['some_choices'].choices = [[choice.pk, choice.description] for choice in self.instance.user.somechoice_set.all()]
class Meta:
model = Spam
This is to populate the new form with choices that pertain to the current request.user (which is set on the instance that's passed into the form). Is there a better way?
Use a ModelMultipleChoiceField in the form and set its queryset in __init__().