I have a form, where validation depends on logged user. For some users are certain values valid, for other users they are invalid. What is valid and what is invalid is dynamic - I can't create new form for each user group.
What's more I need this same validation in more forms, so I created custom form field. To this custom form field I need to pass user instance somehow to check if the value is valid or not.
How to do this?
I am doing it like that:
class EventForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
user = kwargs.pop('user', None)
# doing stuff with the user…
super(EventForm, self).__init__(*args, **kwargs)
In your view class/method you have to instantiate the form like this:
form = EventForm(user=request.user, data=request.POST)
You don't need an extra field for this.
You can access the current user by passing it explicitly or by fetching it from the request in your form's init() method.
Then you can use the retrieved value when cleaning your form.
If you need this functionality in several forms I'd create either a base class that the specialized forms inherit from or create a mixin that adds the desired functionality.
Related
Suppose you have a ModelForm to which you have already binded the data from a request.POST. If there are fields of the ModelForm that I don't want the user to have to fill in and that are therefore not shown in the form (ex: the user is already logged in, I don't want the user to fill a 'author' field, I can get that from request.user), what is the 'Django' way of doing it ?
class RandomView(View):
...
def post(self, request, *args, **kwargs):
form = RandomForm(request.POST)
form.fill_remaining_form_fields(request) ### How would you implement this ???
if form.is_valid():
...
I have tried adding the fields to the form instance (ex: self.data['author'] = request.user) but given its a QueryDict it is immutable so it clearly isn't the correct way of doing this.
Any suggestions ?
My bad, the Django documentation actually explains how to do this in https://docs.djangoproject.com/en/4.0/topics/forms/modelforms/#selecting-the-fields-to-use (see the Note).
I have a view that inherits from UpdateView and UserPassesTestMixin. Two types of user can access it - either a superuser or a normal one. I determine which one it is in test_func() and store the result in self.superuser. Now I want to modify the displayed form depending on the level of the user (namely disable almost all fields in the form if the current user is not a superuser, otherwise leave them all enabled). Which view method should I override to modify the view's form (in this case disable the necessary fields)? Or is this idea wrong and I should approach this differently?
You can pass the data you need to recognize the user to the form and in your form, you can get the data in the __init__ then you can change the self.fields in there to reflect the type of user.
Check this example that removes the ReadOnly to the field superuser_field if the form is instantiated for a superuser.
class MyForm(forms.Form):
def __init__(self, is_superuser=False, *args, **kwargs):
super(MyForm, self).__init__(*args, **kwargs)
if is_superuser:
self.fields['superuser_field'].disabled = False
In your code you can, if I understood correctly:
def yourview(...):
# ...
myform = MyForm(is_superuser=self.superuser)
# ....
I'm wondering how to change the behavior of a form field based on data in the request... especially as it relates to the Django Admin. For example, I'd like to decrypt a field in the admin based on request data (such as POST or session variables).
My thoughts are to start looking at overriding the change_view method in django/contrib/admin/options.py, since that has access to the request. However, I'm not sure how to affect how the field value displays the field depending on some value in the request. If the request has the correct value, the field value would be displayed; otherwise, the field value would return something like "NA".
My thought is that if I could somehow get that request value into the to_python() method, I could directly impact how the field is displayed. Should I try passing the request value into the form init and then somehow into the field init? Any suggestions how I might approach this?
Thanks for reading.
In models.py
class MyModel(models.Model):
hidden_data = models.CharField()
In admin.py
class MyModelAdmin(models.ModelAdmin):
class Meta:
model = MyModel
def change_view(self, request, object_id, extra_context=None):
.... # Perhaps this is where I'd do a lot of overriding?
....
return self.render_change_form(request, context, change=True, obj=obj)
I haven't tested this, but you could just overwrite the render_change_form method of the ModelAdmin to sneak in your code to change the field value between when the change_view is processed and the actual template rendered
class MyModelAdmin(admin.ModelAdmin):
...
def render_change_form(self, request, context, **kwargs):
# Here we have access to the request, the object being displayed and the context which contains the form
form = content['adminform'].form
field = form.fields['field_name']
...
if 'obj' in kwargs:
# Existing obj is being saved
else:
# New object is being created (an empty form)
return super(MyModelAdmin).render_change_form(request, context, **kwargs)
I am wondering how to access a user's profile when creating the
queryset for a ModelChoiceField. I would like to be able to use the
ModelChoiceField to display contacts from another table based on a
parameter saved in the user profile i.e.
who = forms.ModelChoiceField(queryset=Contacts.objects.filter(id__exact=request.user.get_profile().main_company))
Is there a better way to do this (beyond an ajax picker in the
template)?
Greg
For Those interested I was able to come up with a solution from the following SO discussions:
How do I access the request object or any other variable in a form's clean() method?
Django: accessing the model instance from within ModelAdmin?
class InspectionRequestForm(ModelForm):
....
def __init__(self, *args, **kwargs):
self.request = kwargs.pop('request', None)
super(InspectionRequestForm, self).__init__(*args, **kwargs)
companyid = self.request.user.get_profile().main_contactnum.clientid.idflcustomernum
self.fields['who'].queryset = Contacts.objects.filter(clientid__exact=companyid)
My View:
Save Form (Not as necessary to include request=request here, but just in case)
form = InspectionRequestForm(request.POST, request=request)
Or Empty Form
form = InspectionRequestForm(request=request)
Thanks to Daniel Roseman for both of the previous answers.
https://stackoverflow.com/users/104349/daniel-roseman
Django lets you create a model foreign-keyed to User and define it in settings as the official "profile" model holding additional data for user accounts. django-profiles lets you easily display/create/edit that profile data. But the user's primary email address is part of their main account, not part of their extended profile. Therefore when you put
{{ form }}
in the profile/edit_profile template, the primary email address does not show up. You can retrieve it manually with
{{ user.email }}
but changes to it aren't saved back to the account upon submit of course. I'm assuming a custom ModelForm has been created, such as:
class ProfileForm(ModelForm):
class Meta:
model = Parent
exclude = ('family','user','board_pos','comm_job',)
and that ProfileForm is being passed to django-profiles' view code with a urlconf like:
('^profiles/edit', 'profiles.views.edit_profile', {'form_class': ProfileForm,}),
The same problem would come up if you wanted to let users change their first or last names. What's the best way to let users change their own email addresses or names when using django-profiles?
Here's the solution we ended up using:
# urls.py
# First match /profiles/edit before django-profiles gets it so we can pass in our custom form object.
('^profiles/edit', 'profiles.views.edit_profile', {'form_class': ProfileForm,}),
(r'^profiles/', include('profiles.urls')),
Now we override the save method in the form itself, so that when the form is saved, the email address is pushed into the saving user's User object at the same time. Graceful.
# forms.py , trimmed for brevity
class ProfileForm(ModelForm):
def __init__(self, *args, **kwargs):
super(ProfileForm, self).__init__(*args, **kwargs)
try:
self.fields['email'].initial = self.instance.user.email
except User.DoesNotExist:
pass
email = forms.EmailField(label="Primary email")
class Meta:
model = Parent
def save(self, *args, **kwargs):
"""
Update the primary email address on the related User object as well.
"""
u = self.instance.user
u.email = self.cleaned_data['email']
u.save()
profile = super(ProfileForm, self).save(*args,**kwargs)
return profile
Works perfectly. Thanks mandric.
I think that implementing a Separate page just for change of email is best, since it would need to be verified etc...
If you would like to enable users to modify all their profile info together with their main email address, then you need to create your own Form (ModelForm will not work here). I suggest you start doing this and post a question when you get stuck.
Start by copying all the fields out of django-profile model into your custom form, and add the users primary email field.
You will have to "override" the django-profile edit url and basically copy the html template if there is one.
Another option (bad) would be to hack django-profiles app and change it there. But that will, likely, introduce a lot of bugs, and will render your app unapgradable.
I think the easiest way would definitely be to use a form. Use the form to display their current email address (which they could change), and then use your view to extract the request, retrieve the appropriate profile belonging to that user by matching some other parameter you could pass to the template, and then storing the new data and saving the model.