Django Form Clean Method Test Error - python

I'm testing a form that I wrote up earlier. For some reason, the test won't pass. It's like the form is ignoring the data I pass it, and I don't see why. The traceback tells me that the user variable in the clean method of the form is None, though a User is definitely passed into the form. The traceback:
... in clean
if user.pk is not userwebsite.user.pk: AttributeError: 'NoneType' object has no attribute 'pk'
The Form:
class CreateAuditForm(forms.Form):
user = forms.ModelChoiceField(queryset=User.objects.all(), widget=HiddenInput)
website = forms.ModelChoiceField(queryset=UserWebsite.objects.all(), widget=HiddenInput)
emails = forms.CharField(
max_length=250,
required=False
)
def clean_user(self):
user = self.cleaned_data.get('user', None)
if not user.groups.filter(name__iexact='subscribed').exists() and not user.groups.filter(name__iexact='addon').exists():
raise forms.ValidationError(_("You must have an active subscription to request \
website audits. Please try again after subscribing to us."))
return user
def clean(self):
data = self.cleaned_data
user = data.get('user')
userwebsite = data.get('website', None)
if userwebsite.user:
if user.pk is not userwebsite.user.pk:
raise forms.ValidationError(_("Sorry, try again."))
elif userwebsite.addon:
if user.pk is not userwebsite.addon.pk:
raise forms.ValidationError(_("Sorry, try again."))
return self.cleaned_data
def save(self):
# Action
The Test:
class CreateAuditFormTestCase(TestCase):
def setUp(self):
super(CreateAuditFormTestCase, self).setUp()
self.form = CreateAuditForm
...
self.website = Website.objects.create(
title="permanence",
url="https://www.badabuyhere.com",
display="www.bababuyhere.com")
self.unsubscriber = User.objects.create(
username="adiagojesse",
first_name="adiago",
last_name="jesse",
email="bannerfare#coldmount.com",
password="tigermountainvalley"
)
self.unsubscriberwebsite = UserWebsite.objects.create(
user=self.unsubscriber,
website=self.website,
is_competitor=False
)
...
def test_user_validation(self):
data = {
"user":self.unsubscriber.pk,
"website":self.unsubscriberwebsite.pk,
"emails":"john#gmail.com, jeff#gmail.com"
}
self.assertTrue(self.unsubscriber)
self.assertTrue(self.unsubscriberwebsite)
audit = self.form(data)
self.assertEqual(audit.is_valid(), False)
This is probably a simple issue that I can't pick up on, which is what's frustrating me, lol. Help would be appreciated.

My guess is that in CreateAuditForm.clean, useris None because clean_user raised a ValidationError. The ValidationError comes from the fact that the user does not have the groups he needs.
Another issue I see is that to test equality between model instances in Django, you should not use primary keys but test using the instances directly, using == and not is. (See https://stackoverflow.com/a/13650309/1644198 for more information on is and ==)
Example:
if user != userwebsite.user:
# etc...

Related

Django - Questionnaire - ModelFormSet - Cannot assign

Alright, so I'm following a tutorial on creating a small questionnaire with Django.
A User can create a survey with different multiple choice questions. Since they're multiple choice questions, users can also set the options for the questions.
A "survey taker" can then start a survey, select his or her preferred questions and submit the form.
I would like to only show a single question per page so I'm trying to work with modelformset_factory and implement pagination using the build in paginator.
The form is rendered correctly and a user can submit an answer, however, the form fails before "formset.is_valid()", I just can't figure out why;
ValueError at /surveys/1/submit/4/
Cannot assign "'2'": "Answer.option" must be a "Option" instance.
So I can not save an integer and I somehow have to relate the integer with the id from the option model... But I can't access cleaned_data yet so I guess I'm missing something here. Did I forget something within the formset?
I've been staring at this for a while now so any help is appreciated.
#Views
def submit(request, survey_pk, sub_pk):
# Let's retrieve the survey which is created by the survey-taker
try:
survey = Survey.objects.prefetch_related("question_set__option_set").get(
pk=survey_pk, is_active=True
)
except Survey.DoesNotExist:
raise Http404()
try:
submission = survey.submission_set.get(pk=sub_pk, is_complete=False)
except Submission.DoesNotExist:
raise Http404()
# Retrieve all question associated with this survey
questions = survey.question_set.all()
# Get all related options from the questions
options = [q.option_set.all() for q in questions]
form_kwargs = {"empty_permitted": False, "options": options,}
# Setup a formset utilizing a ModelFormSet
AnswerFormset = modelformset_factory(
Answer,
form=AnswerModelForm,
formset=BaseAnswerFormSet,
exclude=(),
extra=len(questions),
)
if request.method == "POST":
print("Request.POST", request.POST)
# This is where the problem starts
formset = AnswerFormset(request.POST, form_kwargs=form_kwargs)
print("Formset: ", formset)
if formset.is_valid():
print("Formset is valid")
with transaction.atomic():
for form in formset:
Answer.objects.create(
option_id=form.cleaned_data["option"],
submission_id=sub_pk,
)
submission.is_complete = True
submission.save
return redirect("survey-thanks", pk=survey_pk)
else:
formset = AnswerFormset(form_kwargs=form_kwargs)
print(form_kwargs)
question_forms = zip(questions, formset)
return render(
request,
"survey/submit.html",
{
"survey": survey,
"question_forms": question_forms,
"formset": formset
}
)
#forms
# Setup a Modelformset
class AnswerModelForm(forms.ModelForm):
class Meta:
model = Answer
fields = "option",
def __init__(self, *args, **kwargs):
options = kwargs.pop("options")
# Options must be a list of Option objects
choices = {(o.pk, o.text) for o in options}
super().__init__(*args, **kwargs)
option_field = forms.ChoiceField(choices=choices, widget=forms.RadioSelect, required=True)
self.fields["option"] = option_field
class BaseAnswerFormSet(forms.BaseFormSet):
def get_form_kwargs(self, index):
kwargs = super().get_form_kwargs(index)
kwargs["options"] = kwargs["options"][index]
return kwargs
#models
class Answer(models.Model):
"""An answer a survey's questions."""
submission = models.ForeignKey(Submission, on_delete=models.CASCADE)
option = models.ForeignKey(Option, on_delete=models.CASCADE)
option_id=form.cleaned_data["option"],
is the suspect, it may require an option instance, even though you are trying to pass the id. Try changing option_id to option__id and explicitly setting the form.cleaned_data["option"] to int. Another way to to is to pull the option manually and pass it directly:
option = Option.object.get(id=int(form.cleaned_data["option"]))

function inside the python class is not called while running the server

i am trying to validate unique user name in the registration form. while i click the submit button after entering the details in the register form, instead of raising a validation error with the given message, it fails and errors out Validationerror at /register/. i am also trying to print the string inside the function. but it doesnt print that too. is it actually calling the clean function. i am using self to achieve this. it should be called !. am i misiing something?
class Register_Form(forms.Form):
user_id = forms.CharField(label="User Name",widget = forms.TextInput(
attrs = {"class":"form-control"}
))
email_id = forms.CharField(label = "Email",widget = forms.EmailInput(
attrs = {"class":"form-control"}
))
password = forms.CharField(label = "Password",widget = forms.PasswordInput(
attrs = {"class":"form-control"}
))
confirm_pass = forms.CharField(label = "Confirm Password",widget = forms.PasswordInput(
attrs = {"class":"form-control"}
))
def clean_username(self):
user_id = self.cleaned_data["user_id"]
print("Entered")
ue = User.objects.filter(username=user_id)
if ue.exists():
raise forms.ValidationError("User name not available")
return user_id
It's not being called, the fact you pass self doesn't mean it'll automatically run, you are just extending your function with self so that it can inherit it's attributes.
Somewhere inside your class you'll have to run self.clean_username(), alternatively if you construct the class, e.g. RF = Register_Form() you'll have to call RF.clean_username()

Django Form some field always get None in clean_field() or clean()

It's always get None in clean() or clean_field(), but it's in self.data.
django 2.x
The fields declaration is:
phone = forms.CharField(
label='',
min_length=phone_number_min,
max_length=phone_number_max,
)
code_length = settings.SMS_CODE_LENGTH
code = forms.CharField(
label='',
min_length=code_length,
max_length=code_length,
)
def clean_code(self):
code = self.cleaned_data.get('code')
phone = self.cleaned_data.get('phone')
result, message = sms_validator.validate(phone, code)
def clean(self):
code = self.cleaned_data.get('code')
phone = self.cleaned_data.get('phone')
result, message = sms_validator.validate(phone, code)
Both of above all run in error:
phone = None
But if
phone = self.data.get('phone')
It's can get the value.
I want to get the phone value in clean_data
Firstly, you must always return the cleaned value from a field clean method. Secondly, it is not safe to access other field values in a field clean method; that is what the overall clean() method is for.

Using request in django model

I've got 2 models
class Post(models.Model):
user = models.ForeignKey(User, verbose_name='User')
content = models.TextField(verbose_name='Text', max_length=4000)
date = models.DateTimeField(verbose_name='Date', default=now())
class Vote(models.Model):
vote = models.IntegerField(choices=VOTE, verbose_name='Vote')
post = models.ForeignKey(Post, verbose_name='Post')
user = models.ForeignKey(User, verbose_name='User')
and view which load last 15 posts:
posts = Post.objects.all().order_by('-date')[:15]
Now I want to have information about active user vote in all of my queryset objects
I thought about custom method in Post model which will check logged user and get vote objects:
def user_vote(self):
try:
data = Vote.objects.filter(post=self, user=request.user)
return data.vote
except ObjectDoesNotExist:
#not voted
But it seems django dont allow to use request in model.
Any other ideas?
You can pass user as argument to method like this:
def user_vote(self, user):
also I guess you need to get a Vote instance, not queryset, so you should user .get method instead .filter:
def user_vote(self, user):
try:
data = Vote.objects.get(post=self, user=user)
return data.vote
except ObjectDoesNotExist:
#not voted
then you can call this method with Post instance:
post.user_vote(request.user)
UPDATE
Django not allow to use method with arguments in template. So in your case better to refactor this method into templatetag:
def get_user_vote(post, user):
try:
data = Vote.objects.get(post=post, user=user)
return data.vote
except ObjectDoesNotExist:
#not voted
You can find manual here https://stackoverflow.com/a/6493001/3291969
don't forget to register it :)
That's not a django problem, it's because request isn't anywhere in the scope of your code. If you pass request to your model's method it will work. Something like:
def user_vote(self, request):
try:
data = Vote.objects.get(post=self, user=request.user)
return data.vote
except Vote.DoesNotExist:
#not voted
and call it like this with your Post instance:
# Note the request being passed as a parameter
vote = post.user_vote(request)
UPDATE:
To get all votes from a user you could do something like this:
votes = Vote.objects.filter(post=self, user=request.user)
return reduce(lambda x, y: x.vote + y.vote, votes, 0)
we set a 0 at the end, so in cases where there's no votes from this user, we'll get a 0 as a return.
This will sum every vote and return it.
If you don't want to sum these votes, change the line:
return reduce(lambda x, y: x.vote + y.vote, votes, 0)
to:
return map(lambda x, y: x.vote + y.vote, votes)
and this will get you a list with all the votes from the request.user user
I think you're looking for this package:
https://pypi.python.org/pypi/django-crequest/1.0
Description:
crequest will bring you current request object of your django application from anywhere in your code.
And it is used like this:
from crequest.middleware import CrequestMiddleware
current_request = CrequestMiddleware.get_request()

Missing cleaned_data in forms (django)

I would like to create a form and the validation_forms that would check if some text apears in a box if another box has been checked correctly,
class Contact_form(forms.Form):
def __init__(self):
TYPE_CHOICE = (
('C', ('Client')),
('F', ('Facture')),
('V', ('Visite'))
)
self.file_type = forms.ChoiceField(choices = TYPE_CHOICE, widget=forms.RadioSelect)
self.file_name = forms.CharField(max_length=200)
self.file_cols = forms.CharField(max_length=200, widget=forms.Textarea)
self.file_date = forms.DateField()
self.file_sep = forms.CharField(max_length=5, initial=';')
self.file_header = forms.CharField(max_length=200, initial='0')
def __unicode__(self):
return self.name
# Check if file_cols is correctly filled
def clean_cols(self):
#cleaned_data = super(Contact_form, self).clean() # Error apears here
cleaned_file_type = self.cleaned_data.get(file_type)
cleaned_file_cols = self.cleaned_data.get(file_cols)
if cleaned_file_type == 'C':
if 'client' not in cleaned_file_cols:
raise forms.ValidationError("Mandatory fields aren't in collumn descriptor.")
if cleaned_file_type == 'F':
mandatory_field = ('fact', 'caht', 'fact_dat')
for mf in mandatory_field:
if mf not in cleaned_file_cols:
raise forms.ValidationError("Mandatory fields aren't in collumn descriptor.")
def contact(request):
contact_form = Contact_form()
contact_form.clean_cols()
return render_to_response('contact.html', {'contact_form' : contact_form})
Infortunatly, django keeps saying me that he doesn't reconize cleaned_data. I know i've missed something about the doc or something but i cannot get the point on what. Please help !
When validating an individual field, your clean method should have a name of the form
clean_<name of field>
for example clean_file_col. Then it will be called automatically when you do form.is_valid() in your view.
Naming your method clean_cols suggests that you have a field named cols, which could cause confusion.
In this case, your validation relies on other fields, so you should rename your clean_col method to simply clean. That way it will be called automatically when you do form.is_valid() in your view.
def clean(self):
cleaned_data = super(Contact_form, self).clean()
cleaned_file_type = self.cleaned_data.get(file_type)
# ...
Finally, in your view, you have not bound your form to any data,
contact_form = Contact_form()
so contact_form.is_valid() will always return False. You need to bind your form to the post data with form = ContactForm(request.POST). See the Django docs for using a form in a view for a full example and explanation.

Categories

Resources