Create method in forms.Form - django

My questions is, can I create methods in a form?
I want to create a method in a UserChangeForm it should control some things.
And how can I call this method then in the save() if commit?
In the forms.form I must create a save() too?
I tried this:
class UserChangeForm(forms.Form):
#fields for the form
def create_club_contact(self):
user = super(UserChangeForm, self).save(commit=False)
if information is None:
#create clubcontact
else:
#update clubcontact
return clubcontact
def clean_password2(self):
#check password
return password2
def save(self):
data = self.cleaned_data
user = TennisClub(link=data['link'], name=data['name'])
user.save()
contact = self.create_club_contact()
user.club_contact = contact
user.save()
On this way I'm always getting this Error
'super' object has no attribute 'save'
I think the forms.Form needs a save() method, but I don't know, how to do it right.
In the docs, I have not seen such a example.
Thanks for helping.

class MyForm(forms.Form):
def __init__(self):
self.member = False
# example of a method with return statement
def my_method(self):
return something
# example for a void method, no return value
def another_method(self):
self.member = True
def save(self):
my_variable = self.my_method()
self.another_method()
# continue with your logic
EDIT:
I think you don't have the problem with creating methods and calling them. The above is probably clear to you. But it seems that you have another problem, but you can't question it right.
Your form inherits from Form. The parent class Form has a method save() and you want to override it.
Put this in the end of your method save() and try again:
return super(UserChangeForm, self).save()

Related

I am confused what form_valid() or form_invalid() accomplished

I just started to use class based views instead of functions so sorry if this is an obvious question but what does form_valid() and form_invalid() do in a class that inherits from FormView? Django says that:
"This method is called when valid form data has been POSTed. It should
return an HttpResponse"
But what I don't get is how is it validated? I have additional checks I need to perform on the form before I can save the data (such as when signing up, making sure the two passwords entered are the same). How would I be able to add more checks if I want to?
Here's the code I've been working with
forms.py
class SignupForm(forms.Form):
email = forms.EmailField()
alias = forms.CharField(max_length=15)
password = forms.CharField(max_length=128, widget=forms.PasswordInput)
confirm_pass = forms.CharField(max_length=128, widget=forms.PasswordInput)
# used to verify if the passwords match
def pwd_match(self):
if self.is_valid():
cd = self.cleaned_data
if cd.get('password') == cd.get('confirm_pass'):
return True
else:
return False
def fields_correct(self):
if self.pwd_match() and self.is_valid():
return True
return False
views.py
class SignupFormView(AjaxFormMixin, FormView):
form_class = SignupForm
template_name = 'forum/signup.html'
success_url = reverse_lazy('home')
def form_valid(self, form):
# here I want to make sure that the passwords match and then save the user
Thanks ahead of time!
You have two options here,
1- you can add a clean method to your form for the extra validation you need to do. So in your forms, you will have something like,
def clean(self):
cleaned_data = super().clean()
# do some validation on your fields
...
2- you can add the extra logic in your is_valid function in the view and return the user to the same page with an error message. A little messy but in some cases, you need to hack around.
Hope this helps!
This is not what form_valid does. That method is called once the form has been successfully validated.
Whether you use a function-based or a class-based view, this kind of thing belongs in the form. You should remove your pwd_match and fields_correct method, and instead define a single clean method:
def pwd_match(self):
cd = self.cleaned_data
if cd.get('password') != cd.get('confirm_pass'):
raise forms.ValidationError('passwords do not match')
return cd

automatically saved value in a UpdateView Django

i'm new in Django and i'm learning about the views and the methods and how they work, especially with this problem. The thing is that I would like to know how to automatically save a value of a field in my model after updating an object in a UpdateView, for example when I update an object, in this case a report where I can assign a person to do it, I would like to save a model value that shows the "status" and save the value of "assigned" or something like that, to know if the report was already assigned or not. I know there are methods and that maybe one of them could be done by overwriting the class, but I do not know how to apply it or which one to use.
For help this is a simple class of a UpdateViews that i'm using:
class reporteupdate(UpdateView):
model = reporte_fallo
form_class = ReporteAsignar
template_name = 'formulario/jefe_asignar.html'
success_url = reverse_lazy('formulario:reporte_listar_jefe')
and the field of the model that I would like to assign a value to is called status.
i'm waiting for your help, since I'm stuck with that doubt. Thanks!!!
the query dict will be changable after you create a copy of it in post method so you can do this:-
class SomeUpdateView(UpdateView):
model=your model
form_class=you form
def post(self, request, **kwargs):
request.POST = request.POST.copy()
request.POST['status'] = 'Assigned'
return super(SomeUpdateView, self).post(request, **kwargs)
You could perhaps set the status flag after the form has been successfully validated, by overriding the form_valid() method in your reporteupdate view:
class reporteupdate(UpdateView):
...
def form_valid(self, form):
# Call super() to save the model and return the success url
resp = super().form_valid(form)
# Set your status flag
self.object.status = 'assigned'
self.object.save()
return resp

Django: Display user's previous choices for a ModelForm in the template

I am trying to create a user profile page where users can see and update their preferences for certain things, like whether they are vegetarian, or have a particular allergy, etc. I want the data to be displayed as a form, with their current preferences already populating the form fields.
So I've created the following Model:
class FoodPreferences(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE) # One user has one set of food prefs
vegetarian = models.BooleanField()
vegan = models.BooleanField()
...
that's referenced in my forms.py:
class FoodPreferencesForm(forms.ModelForm):
class Meta:
model = FoodPreferences
exclude = ('user', )
I've tried creating a view that inherits FormView and then referencing the form, like this:
class UserProfileView(generic.FormView):
template_name = "registration/profile.html"
form_class = FoodPreferencesForm
success_url = reverse_lazy('user_profile')
This saves the form to a instance of the model correctly, but obviously it just displays the blank form again, after updating, so the user has no idea what their current preferences are.
To implement this I thought I might need to override get() and post() to get the instance of FoodPreferences for the user, and then pass those values into the form like you would a request.POST object. However, firstly, I don't know how to do that, and secondly I'd be taking responsibility for correctly updating the database, which the FormView was already doing.
This is what I've got for that solution:
def get(self, request, *args, **kwargs):
prefs = FoodPreferences.objects.get(user=request.user)
form = self.form_class(prefs)
return render(request, self.template_name, {'form': form, })
def post(self, request, *args, **kwargs):
form = self.form_class(request.POST)
if not form.is_valid():
return render(request, self.template_name, {'form': form, 'error': 'Something went wrong.'})
curr_prefs = FoodPreferences.objects.update_or_create(form.fields)
prefs.save()
return render(request, self.template_name, {'form': form, })
but I get a TypeError: argument of type 'FoodPreferences' is not iterable on the line in get():
form = self.form_class(prefs)
because it's not expecting a model instance.
Am I thinking about this in the right way? This seems like a common enough problem that Django would have something inbuilt to do it, but I can't find anything.
You should only rarely need to define get or post in a class-based view, and you definitely don't here.
To start with, you need to use a more appropriate base class for your view. Here you want to update an existing item, so you should use UpdateView.
Secondly, you need to tell the class how to get the existing object to update, which you can do by definining get_object. So:
class UserProfileView(generic.UpdateView):
template_name = "registration/profile.html"
form_class = FoodPreferencesForm
success_url = reverse_lazy('user_profile')
def get_object(self, queryset=None):
return self.request.user.foodpreferences
# or, if you aren't certain that the object already exists:
obj, _ = FoodPreferences.objects.get_or_create(user=self.request.user)
return obj

has_object_permission not taken into account

I'm trying to enforce a permission with Django Rest Framework where a specific user cannot post an object containing a user id which is not his.
For example i don't want a user to post a feedback with another id.
My model is something like :
class Feedback(Model):
user = ForeignKey(User)
...
I try to put a permission on my view which would compare the feedback.user.id with the request.user.id, the right work ok on a post on an object and return false, but it's still posting my object... Why?
The View
class FeedbackViewSet(ModelViewSet):
model = Feedback
permission_classes = (IsSelf,)
serializer_class = FeedbackSerializer
def get_queryset(self):
....
The Permission
class IsSelf(permissions.BasePermission):
def has_object_permission(self, request, view, obj):
#return eval(obj.user.id) == request.user.id
return False
I've commented the line to show where the problem lies.
Again the function is correctly called and returns False, but there's just no PermissionDenied raised.
While at it, i'm wondering if this is actually the way to implement this behaviour, and if not, what would be...?
Thanks.
Your problem is that has_object_permission is only called if you're trying to access a certain object. So on creation it is never actually used.
I'd suggest you do the check on validation. Example:
class FeedbackSerializer(HyperlinkedModelSerializer):
def validate(self, attrs):
user = self.context['request'].user
if attrs['user'].id != user.id:
raise ValidationError('Some exception message')
return attrs
If you have some other super serializer class then just change it.
Now that I think of it if the user field must always be the posting user, then you should just make that field read-only and set it on pre_save() in the viewset class.
class FeedbackViewSet(ModelViewSet):
def pre_save(self, obj, *args, **kwargs):
if self.action == 'create':
obj.user = self.request.user
And in the serializer set the user field read-only
class FeedbackSerializer(HyperlinkedModelSerializer):
user = serializers.HyperlinkedRelatedField(view_name='user-detail', read_only=True)
....
I don't know if this is still open...
However, in order to work, you should move that line from "has_object_permission" to "has_permission", something like this:
class IsSelf(permissions.BasePermission):
def has_permission(self, request, view, obj):
if request.method == 'POST':
#your condition
Worked for me.
As it was stated in the selected answer
has_object_permission is only called if you're trying to access a certain object
so you have to place your condition under has_permission instead.

Django - ForeignKey field initial value definition in Admin

I have a Person model, which has a ForeignKey field to itself, called mother.
When the user goes to the 'add' admin form, I want to define an initial value for mother, in case there is a GET('mother') parameter, or leave it blank, in case there is not.
I have actually 2 questions:
How to access request inside ModelAdmin?
How to define initial value for a ForeignKey field?
In models.py:
class Person(models.Model):
name=models.CharField()
mother=models.ForeignKey('self')
In admin.py:
class PersonAdminForm(forms.ModelForm):
class Meta:
model = Person
class PersonAdmin(admin.ModelAdmin):
mother = request.GET.get('mother','') #don`t know how to access request
if mother != '':
form = PersonAdminForm
form.initial={'mother':Person.objects.get(id=mother)}
Well, this ain't working. Even if I only try to define a hardcoded initial value, it doesn`t work.
What am I doing wrong?
PS.: Of course, I may be asking the wrong questions, so I appreciate any help that solves the problem.
My solution:
class PersonAdmin(admin.ModelAdmin):
form = PersonAdminForm
# ...
def get_form(self, request, obj=None, *args, **kwargs):
form = super(PersonAdmin, self).get_form(request, *args, **kwargs)
# Initial values
form.base_fields['mother'].initial = None
if obj and obj.mother:
form.base_fields['mother'].initial = obj.mother
return form
Oh, it happens to be a lot easier than I thought.
If you pass a GET parameter with the name of the field as key to a Django`s add form, the GET parameters value will be set as initial value for that field.
In my case, I just needed to redirect to
localhost/admin/my_app/person/add/?&mother=< id >
There was no need for manipulating admin or anything.
Try overriding the get_form() method on ModelAdmin:
class PersonAdmin(admin.ModelAdmin):
form = PersonAdminForm
def get_form(self, request, *args, **kwargs):
form = super(PersonAdmin, self).get_form(request, *args, **kwargs)
mother = request.GET.get('mother', None)
if mother:
form.initial = {'mother': Person.objects.get(id=mother)}
return form