I have a model field
is_anonymous = BooleanField(default=False)
I also have a ModelForm. I want this field to be represented with a select widget.
It would be
class MyModelForm(forms.ModelForm):
class Meta:
model = MyModel
fields = ['is_anonymous']
widgets = {
'is_anonymous': forms.NullBooleanSelect(),
}
It works. But I want the select widget to only have two choices true (1) and false (0) and the represented text in each option should be Anonymous for true and self.request.user for false.
I think I have to do this replacement in the views as self.request.user is not available in the ModelForm.
How can I do this?
It's not 100% clear what you want, but if you want to display a select dropdown with only two choices; "Anonymous" which maps to True and "myusername" (i.e. the username of the current user) which maps to False, you need to override the is_anonymous field's widget's choices attribute:
class MyModelForm(forms.ModelForm):
def __init__(self, user, *args, **kwargs):
super(MyModelForm, self).__init__(*args, **kwargs)
self.fields['is_anonymous'].widget = forms.Select(choices=[
(True, "Anonymous"),
(False, user.username)
])
class Meta:
model = MyModel
fields = ['is_anonymous']
because we need the user object in our form, we need to pass it manually as a parameter when defining the form in our view. This depends on the type of view you are using, but assuming it's a generic class based CreateView, you need the following:
class MyCreateView(CreateView):
form_class = MyModelForm
...
def get_form(self, form_class):
return self.form_class(self.request.user, **self.get_form_kwargs())
...
Related
this is my views.py :
i want save type in device field in model
class GetDeviceMixin( object):
def setup(self, request, *args, **kwargs):
super().setup( request, *args, **kwargs)
type= request.META['HTTP_USER_AGENT']
print(type)
return type
class RegisterView(GetDeviceMixin , generic.CreateView):
form_class = CustomUserCreationForm
success_url = reverse_lazy("register")
template_name = "man/man.html"
and this is my models.py
class account(AbstractBaseUser):
first_name= models.CharField(max_length=20,verbose_name="first name")
device = models.CharField(verbose_name="device" , max_length=100)
this is my forms.py:
class GetReq(forms.ModelForm):
class Meta:
model = account
fields = ['device',]
First, pop Classy CBVs onto your browser bookmark list ...
I'm assuming that you want to use a form, either to get other information from the user, or to allow the user to override the automatically determined value of device. In this case, you want to pass it as the initial value for device to the form
Now, look at CreateView to work out what to subclass. get_initial() looks hopeful, so
def get_initial(self):
initial = super().get_initial()
initial['device'] = self.device_type # as per the comment!
return initial
You should now see a form with the automatically determined value as the default value
If the intent was to get other fields of the model from the user and to always forcibly insert the automatically determined device_type, you would instead subclass form_valid
def form_valid(form):
obj = form.save( commit=False)
obj.device = self.device_type
obj.save()
I want to use UpdateView in my model Event. This model had this field:
employee = models.ForeignKey(User, on_delete=models.CASCADE, related_name='event_employee')
My view :
class UpdateEvent(UpdateView):
model = Event
template_name = 'dashboard/pro_update_event.html'
form_class = UpdateEventForm
other_variable = None
def get_form_kwargs(self):
kwargs = super(UpdateEvent, self).get_form_kwargs()
names_clients = User.objects.filter(professionnels=self.request.user)
kwargs.update({'names_clients': names_clients})
return kwargs
def get_success_url(self, *args, **kwargs):
return reverse_lazy('pro_details_event', kwargs={'pk': self.object.pk})
My Form :
class UpdateEventForm(forms.ModelForm):
"""
edit an event
"""
class Meta():
model = Event
fields = ('employee', 'date_start', 'date_end')
def __init__(self, names_clients, *args, **kwargs):
super(UpdateEventForm, self).__init__(*args, **kwargs)
self.fields['employee'] = forms.ChoiceField(choices=tuple([(client.pk,client.last_name.capitalize()+" "+client.first_name.capitalize()) for client in names_clients]))
It seems work, the widget "select" contain the correct values.
example : <option value="2">Dupond Jean</option>
But when I submit the form :
Cannot assign "'2'": "Event.employee" must be a "User" instance.
I don't understand because if remove "get_form_kwargs" in my view and "def init" in my form, the value passed is the same (the pk of the employee). It's works with this way.
But the problem is all employee are selectable and the username is display not the firstname and lastname.
It's because the employee ForeignKey on your Event model points to the User model, but you're populating the choice field for the employee field with Particulier model data.
Either change the foreign key to point to the Particulier model, or pass in Users to the form kwargs
Sorry, I forgot to fix this error in the code in this page.
But I have the error in my 1st comment, with populating the choice field with an User model data.
Let's say I have a form such as:
from django import forms
class Foo(forms.ModelForm):
class Meta:
model = SomeModel
fields = ('field1', 'field2', 'field3')
How can I include or exclude fields based on some condition?
Edit:
My bad, I should have clarified that the condition needs to be made based on a value on each model that is being rendered. That means that I need to add (or remove) fields based on the values of the model that is currently being rendered.
You can modify form fields via __init__:
from django import forms
class Foo(forms.ModelForm):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
if condition:
self.fields.pop('field1')
class Meta:
model = SomeModel
fields = ('field1', 'field2', 'field3')
Note that this can cause the form's validation to fail if the field is required.
A better approach might be to hide the field, instead of removing it entirely:
class Foo(forms.ModelForm):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
if condition:
form.fields['field1'].widget = forms.HiddenInput()
class Meta:
model = SomeModel
fields = ('field1', 'field2', 'field3')
Let's say I create an model MyModel instance that has a FileField using CreateView and associated MyModelCreateForm, and now I want to update it without uploading the same avatar image:
class MyModel(models.Model):
name = models.CharField()
avatar = models.ImageField()
class MyModelCreateForm(forms.ModeForm):
class Meta:
model = MyModel
exclude = None
class MyModelCreate(CreateView):
model = MyModel
form_class = MyModelCreateForm
class MyModelCreate(UpdateView):
model = MyModel
form_class = ?
On the CreateView's form the image is compulsory. If I want to make it optional on the update field, what's the most Django-ish way of doing this?
Do I need to make a new form for the UpdateView that inherits from MyModelCreateForm but overrides the ImageField required attribute? Or is there a more "batteries-included" way?
Here a solution to make a field required :
class Foo(CreateView):
model = Fiz
fields = ('bar',)
def get_form(self, form_class=None):
form = super(Foo, self).get_form(form_class)
form.fields['bar'].required = True
return form
i think no but you can implement in it !
class ModelXForm(ModelForm):
class Meta:
model = x
fields = ['image','name']
def __init__(self,update, survey, **kwargs):
super(FBRequestForm, self).__init__(**kwargs)
if not update:
self.fields['image'].widget.attrs['readonly'] = True
self.fields['image'].widget = forms.HiddenInput()
note : This is closely related to the answer in this question :
django admin - add custom form fields that are not part of the model
In Django it is possible to create custom ModelForms that have "rouge" fields that don't pertain to a specific database field in any model.
In the following code example there is a custom field that called 'extra_field'. It appears in the admin page for it's model instance and it can be accessed in the save method but there does not appear to be a 'load' method.
How do I load the 'extra_field' with data before the admin page loads?
# admin.py
class YourModelForm(forms.ModelForm):
extra_field = forms.CharField()
def load(..., obj):
# This method doesn't exist.
# extra_field = obj.id * random()
def save(self, commit=True):
extra_field = self.cleaned_data.get('extra_field', None)
return super(YourModelForm, self).save(commit=commit)
class Meta:
model = YourModel
class YourModelAdmin(admin.ModelAdmin):
form = YourModelForm
fieldsets = (
(None, {
'fields': ('name', 'description', 'extra_field',),
}),
)
source code by #vishnu
Override the form's __init__ method and set the initial property of the field:
class YourModelForm(forms.ModelForm):
extra_field = forms.CharField()
def __init__(self, *args, **kwargs):
super(YourModelForm, self).__init__(*args, **kwargs)
initial = '%s*rnd' % self.instance.pk if self.instance.pk else 'new'
self.fields['extra_field'].initial = initial