My form instance is never valid - django

I'm making a social network in django and i've a trouble with the User Creation Form.
Here's my code:
views.py
def register(request):
if request.method == 'POST':
form = UserProfileForm(request.POST) #
if form.is_valid():
u = UserProfile.objects.create(first_name = form.cleaned_data['first_name'],...)
u.set_password(u.password)
u.save()
return HttpResponseRedirect(...)
else:
return Http404
else:
form = UserProfileForm()
return render_to_response(...,
{'form': form},
context_instance=RequestContext(request))
forms.py
class UserProfileForm(ModelForm):
first_name = forms.CharField(help_text="Por favor, introduzca su nombre", required=True)
...
class Meta:
model = UserProfile
fields = ['first_name',...]
def __init__(self, request=None, *args, **kwargs):
self.user = kwargs.pop('user', None)
super(UserProfileForm, self).__init__(*args, **kwargs)
def validate_user(self):
first_name_length = len(self.cleaned_data['first_name'])
...
if not first_name_length > 0:
raise ValidationError("Por favor, introduzca nombre valido")
...
models.py
class UserProfile(models.Model):
first_name = models.CharField(max_length=50, blank=True)...
and i'm using templates with form.as_p, but my form instance is never valid. What am I doing wrong? Can anyone help me?

Look at the signature of your form:
def __init__(self, request=None, *args, **kwargs)
and now look at how you instantiate it:
form = UserProfileForm(request.POST)
So, you're passing request.POST as the request parameter.
You should really avoid changing the signature of a subclass like this. In particular in your case as you are not using or passing the request at all. If you do need to do this, you should do the same as with the user value which you get from kwargs (but again you're not using or passing that either, so I don't know why you're bothering to do it.)

Related

Filtering dropdown queryset based on URL parameter

I am building a CRM where I want each client to have multiple plans, and each plan to have multiple notes. When a user creates a new note, I want them to be able to select a relevant plan from a dropdown of plans belonging to the client. From what I can find, I should be able to get the contact_id from the kwargs, but my errors show nothing in kwargs. I know there should be a way to do this, but I can't seem to find it.
Variable Value
__class__ <class 'lynx.forms.SipNoteForm'>
args ()
kwargs {}
self <SipNoteForm bound=False, valid=Unknown, fields=(sip_plan;note;note_date;fiscal_year;quarter;class_hours;instructor;clients)>
Views.py
#login_required
def add_sip_note(request, contact_id):
form = SipNoteForm()
if request.method == 'POST':
form = SipNoteForm(request.POST)
if form.is_valid():
form = form.save(commit=False)
form.contact_id = contact_id
form.user_id = request.user.id
form.save()
return HttpResponseRedirect(reverse('lynx:client', args=(contact_id,)))
return render(request, 'lynx/add_sip_note.html', {'form': form})
Forms.py
class SipNoteForm(forms.ModelForm):
class Meta:
model = SipNote
exclude = ('created', 'modified', 'user', 'contact')
def __init__(self, *args, **kwargs):
super(SipNoteForm, self).__init__(*args, **kwargs)
self.fields['sip_plan'].queryset = SipPlan.objects.filter(contact_id=kwargs.get("contact_id"))
Urls.py
path('add-sip-note/<int:contact_id>/', views.add_sip_note, name='add_sip_note'),
You are trying to get the kwargs in __init__(self, *args, **kwargs) as
def __init__(self, *args, **kwargs):
contact_id = kwargs.pop('contact_id')
super(SipNoteForm, self).__init__(*args, **kwargs)
self.fields['sip_plan'].queryset = SipPlan.objects.filter(contact_id=contact_id)
But you are not passing contact_id kwargs to the form while posting. you should pass kwargs to the form you are going to get in __init__(self, *args, **kwargs) such as
#login_required
def add_sip_note(request, contact_id):
form = SipNoteForm()
if request.method == 'POST':
form = SipNoteForm(request.POST, contact_id=contact_id)

form.is_valid () always returns False in Django

I got a strange bug. I am failing validation if I add an email field. If validation for only 1 username field, then everything works fine. Tell me please, what am I doing wrong?
file forms.py:
class UserUpdateForm(forms.ModelForm):
email = forms.EmailField(required=False)
def __init__(self, user, *args, **kwargs):
self.user = user
super(UserUpdateForm, self).__init__(*args, **kwargs)
if 'label_suffix' not in kwargs:
kwargs['label_suffix'] = '*'
self.fields['username'].widget = forms.TextInput(attrs={'class':'input-text'})
self.fields['email'].widget = forms.EmailInput(attrs={'class':'input-text'})
class Meta:
model = User
fields = ("username","email",)
file views.py:
def get_context_data(self, request):
self.object = get_object_or_404(Profile,slug=self.kwargs['slug'])
ctx={}
ctx['UserUpdateForm']=UserUpdateForm(request.POST if "UserUpdateForm" in request.POST else None,instance=request.user)
сtx['comments']=Comments.objects.filter(profile=self.object)
return ctx
def post(self, request, *args, **kwargs):
if request.method=='POST':
if "UserUpdateForm" in request.POST:
form=UserUpdateForm(request.POST)
if form.is_valid():
user=User.objects.get(username=self.request.user)
user.username=form.cleaned_data.get('username')
user.email=form.cleaned_data.get('email')
user.save()
obj=Profile.objects.get(user__username=user.username)
return HttpResponseRedirect(reverse_lazy('profile',kwargs={'slug': obj.slug},))
return render(request,self.template_name,self.get_context_data(request))
You construct your form with an extra parameter user:
class UserUpdateForm(forms.ModelForm):
def __init__(self, user, *args, **kwargs):
# …
so that means that the first parameter when you construct the form is the user. You thus should pass a user:
form=UserUpdateForm(request.user, request.POST)
or if you want to edit the user object:
form=UserUpdateForm(request.user, request.POST, instance=request.user)
it however does not make much sense to pass the user, since as far as one can see, you never use the .user attribute in your form.

How to give the information about request.user to clean method

I have a form:
class CreateConferenceForm(forms.ModelForm):
class Meta:
model = Conference
fields = ['name', 'participants']
def clean(self):
cleaned_data = super(CreateConferenceForm, self).clean()
if not request.user.id in cleaned_data.get('participants'):
raise forms.ValidationError('Error')
But I don't know how to import a request object from view, because method is_valid hasn't additional arguments. How I can do it?
Give your form an __init__ method that allows you to pass a user:
def __init__(self, user, *args, **kwargs):
self.user = user
super().__init__(*args, **kwargs)
Now you can use self.user in clean. Wherever you create the form remember to pass the user, e.g. form = CreateConferenceForm(request.user, request.POST) in the view.

how to access/modify django model fields before validating its form in Listview

I have a model Course that has the following attr:
class Course(models.Model):
user= models.ForeignKey(
User,
on_delete=models.CASCADE,
)
# email= models.EmailField(default=user.email)
courseName= models.CharField(max_length=20, blank=False)
class Meta:
unique_together= ('user','courseName',)
def __str__(self):
return self.courseName
I have created a form where I want the user to enter just the courseName and after they POST it, I will add the requested user in the model as well.
This is my form which is getting passed on to the template via my ListView
forms.py
class CourseForm(forms.ModelForm):
class Meta:
model= Course
fields = ['courseName']
**Here is my views.py where I am struggling with **
class CoursesListView(ListView, FormMixin):
model = Course
form_class = CourseForm
template_name = "userApp/courseList.html"
def get_queryset(self):
return Course.objects.filter(user__exact=self.request.user)
def get_context_data(self,*args,**kwargs):
context= super(CoursesListView,self).get_context_data(*args, **kwargs)
context['courseForm'] = self.form_class
return context
def post(self,request, *args, **kwargs):
form = self.form_class(request.POST)
user = User.objects.get(username__exact=self.request.user)
**I want to add the user to my model.user field here**
return self.get(redirect, *args, **kwargs)
def get(self,request, *args, **kwargs):
self.object=None
self.form = self.get_form(self.form_class)
return ListView.get(self, request, *args, **kwargs)
So basically my question is how can I add the user in my model before calling form.is_valid().
something like this?
def post(self,request, *args, **kwargs):
form_data = copy.copy(request.POST)
form_data['user'] = User.objects.get(username__exact=self.request.user).pk
form = self.form_class(form_data)
# form handling follows
return self.get(redirect, *args, **kwargs)
This Answer was suggested by a user who deleted this answer. Didnt get his user id but whoever you were thanks a lot for the help!!!
Just use form.save(commit=False) and then make the necessary changes.
def post(self,request, *args, **kwargs):
form = self.form_class(request.POST)
if form.is_valid():
user = User.objects.get(username__exact=self.request.user)
instance = form.save(commit=False)
instance.user = user
instance.save()

Django formset set current user

Related to this question, but expanding on it - How would I use this technique in a formset?
I'd like to use the current logged in user in a form, but I'm using the form in a formset. The referenced solution for a single form is to pass request.user to the form and process in init. How do I add to the kwargs for each form in the formset?
Example in my code:
in forms.py
class NewStudentForm (forms.Form):
username = forms.RegexField(label=_("Username"), max_length=30, regex=r'^\w+$',
help_text = _("Required. 30 characters or fewer. Alphanumeric characters only (letters, digits and underscores)."),
error_message = _("This value must contain only letters, numbers and underscores."))
first_name = forms.CharField(label=_('first name'), max_length=30 )
last_name = forms.CharField(label=_('last name'), max_length=30, )
email = forms.EmailField(label=_('e-mail address') )
password = forms.CharField(label=_('password'), max_length=64, )
class Meta:
model = User
fields = ("username","first_name", "last_name", "email", "password")
def __init__(self, *args, **kwargs):
self._user = kwargs.pop('user')
super(NewStudentForm, self).__init__(*args, **kwargs)
def save(self, commit=True):
user = super(NewStudentForm, self).save(commit=False)
user.set_password(self.cleaned_data["password"])
if commit:
user.save()
profile = Profile.objects.create_profile(user)
profile.activiation_key = profile.ACTIVATED_KEY
profile.authorized = True
profile.save()
user.is_active=True
user.save()
student = models.Student()
student.user = user
student.teacher = self._user
student.plaintext_pwd = self.cleaned_data["password"]
student.save()
return UserWarning
then in views.py
#login_required
def new_student(request):
from django.forms.formsets import formset_factory
try:
if request.method == 'GET':
newStudentFormset = formset_factory(forms.NewStudentForm, extra=2)
formset = newStudentFormset()
return shortcuts.render_to_response('NewStudent.html', { 'newStudentFormSet':formset, 'active_username': request.user.username })
elif request.method == 'POST':
if LOGIN_FORM_KEY in request.POST:
return _handle_login(request)
data = request.POST.copy()
newStudentFormset = formset_factory(forms.NewStudentForm)
formset = newStudentFormset(data) ### Pass current user to formset? ###
if formset.is_valid():
formset.save()
request.user.message_set.create(message="Save successful.")
return shortcuts.redirect(student)
else:
return shortcuts.render_to_response('NewStudent.html', { 'newStudentFormSet':formset, 'active_username': request.user.username, 'error_message':formset.errors})
return http.HttpResponseNotAllowed(['GET', 'POST'])
except models.Student.DoesNotExist:
return http.HttpResponseNotFound('<h1>Requested Student not found</h1>')
By adding a class that extends BaseFormSet you can add custom code to pass a parameter to the form.
in forms.py:
class NewStudentFormSet(BaseFormSet):
def __init__(self, *args, **kwargs):
self.user = kwargs.pop('user', None)
super(NewStudentFormSet, self).__init__(*args, **kwargs)
def _construct_forms(self):
self.forms = []
for i in xrange(self.total_form_count()):
self.forms.append(self._construct_form(i, user=self.user))
Then in views.py:
# ...
data = request.POST.copy()
newStudentFormset = formset_factory(forms.NewStudentForm, formset=forms.NewStudentFormSet)
formset = newStudentFormset(data, user=request.user)
# ...
Thanks to Ashok Raavi.
I rather to iterate forms directly in the view:
for form in formset.forms:
form.user = request.user
formset.save()
It avoid creating unecessary BaseFormSet
It is cleaner
Based on Paulo Cheque answer (which didn't really work for my case).
I loved the idea of not writing a custom BaseFormSet inherited class.
if formset.is_valid():
new_instances = formset.save(commit=False)
for new_instance in new_instances:
new_instance.user = request.user
new_instance.save()
I tried the solution of selfsimilar but the BaseFormSet didn't work in my Django 1.6.
I followed the steps in: https://code.djangoproject.com/ticket/17478 and the way that worked for me is:
class NewStudentFormSet(BaseFormSet):
def __init__(self, *args, **kwargs):
self.user = kwargs.pop('user',None)
super(NewStudentFormSet, self).__init__(*args, **kwargs)
for form in self.forms:
form.empty_permitted = False
def _construct_forms(self):
if hasattr(self,"_forms"):
return self._forms
self._forms = []
for i in xrange(self.total_form_count()):
self._forms.append(self._construct_form(i, user=self.user))
return self._forms
forms = property(_construct_forms)
Here is a similar question about passing form parameters to a formset:
Django Passing Custom Form Parameters to Formset
Personally, I like the second answer on there about building the form class dynamically in a function because it is very fast to implement and easy to understand.