I currently have a form that allows the user to edit their profile (django-profiles). The form is working great, except that I've put a switch in place for States and Provinces depending on whether ot not the user picks Canada or the US.
In my model, I have both State and Province set so that in the table there are 2 separate fields.
In my save method I have the following:
def user_created(sender, user, request, **kwargs):
form = RegistrationFormZ(request.POST)
....stuff here.....
if form.data["country"] == 'US':
data.state = form.data["state"]
data.provinces = None
if form.data["country"] == 'CA':
data.provinces = form.data["provinces"]
data.state = None
....stuff here....
data.save()
user.first_name = form.data['first_name']
user.last_name = form.data['last_name']
user.save()
from registration.signals import user_registered
user_registered.connect(user_created)
The if statements are not working as I expected they would on save.
What's happening is that it correctly saves the value of the option that was selected (state/province) but then fails to update the opposing one to an empty value.
So for example let's say I had a user who had a Canadian address, and then updated it to the US address. The US state gets properly saved, but province does not get updated to None.
Perhaps a more elegant way to accomplish this is using dictionaries. As an aside, are you sure your field name is provinces (plural) instead of singular? Here's an example how to do this with dictionaries, note you could add more fields for different countries without any more if statements and you could even write a separate getter function to return these dictionaries:
def user_created(sender, user, request, **kwargs):
form = RegistrationFormZ(request.POST)
....stuff here.....
data.state = None
data.provinces = None
state_field = {}
state_field['US'] = {}
state_field['US']['src'] = form.data['state']
state_field['US']['dest'] = data.state
state_field['CA'] = {}
state_field['CA']['src'] = form.data['province']
state_field['CA']['dest'] = data.province
state_field[form.data['country'].upper()]['dest'] = state_field[form.data['country'].upper()]['src']
....stuff here....
data.save()
user.first_name = form.data['first_name']
user.last_name = form.data['last_name']
user.save()
from registration.signals import user_registered
user_registered.connect(user_created)
Related
I have a survey app - you create a Survey and it saves the Response. It's registered in Django Admin. I can see the Survey and submit a Response. When I click Response in Admin, I get the following error:
ValueError at /admin/django_survey/response/
Cannot query "response 5f895af5999c49929a522316a5108aa0": Must be "User" instance.
So I checked the SQL database and for django_survey_response I can see that there is a response, but the column user_id is NULL.
I suspected that there's an issue with my Views and/or Forms and I'm not saving the logged in User's details, so I've tried to address that.
However, now I get
NameError at /survey/1/
global name 'user' is not defined
How do I resolve this? I want the form to save Response with the logged in user's ID.
The Traceback:
django_survey\views.py
def SurveyDetail(request, id):
survey = Survey.objects.get(id=id)
category_items = Category.objects.filter(survey=survey)
categories = [c.name for c in category_items]
print 'categories for this survey:'
print categories
if request.method == 'POST':
form = ResponseForm(request.POST, survey=survey) <.........................
if form.is_valid():
response = form.save()
return HttpResponseRedirect("/confirm/%s" % response.interview_uuid)
else:
form = ResponseForm(survey=survey)
print form
django_survey\forms.py
def __init__(self, *args, **kwargs):
# expects a survey object to be passed in initially
survey = kwargs.pop('survey')
self.survey = survey
self.user = user <.........................
super(ResponseForm, self).__init__(*args, **kwargs)
self.uuid = random_uuid = uuid.uuid4().hex
# add a field for each survey question, corresponding to the question
# type as appropriate.
data = kwargs.get('data')
It might be worth noting that previously, instead of user, the model's field was called interviewee. I changed this and ran migrations again.
I am also using userena.
The error message in this instance is python trying to tell you that you are attempting to access a variable user that has not been defined in the scope of your method.
Let's look at the first few lines of the __init__() method:
def __init__(self, *args, **kwargs):
# expects a survey object to be passed in initially
survey = kwargs.pop('survey')
self.survey = survey
self.user = user
We can see where the survey variable is defined: survey = kwargs.pop('survey'). It is passed into the form as a keyword argument and extracted in the forms __init__. However underneath you attempt to do the same thing with user but haven't actually defined it above. The correct code would look like:
def __init__(self, *args, **kwargs):
# expects a survey object to be passed in initially
survey = kwargs.pop('survey')
user = kwargs.pop('user')
self.survey = survey
self.user = user
However, this still won't work because we aren't passing the user variable to the form via kwargs. To do that we pass it in when we initialise the form in your views.py. What isn't clear is what user object you are expecting to pass in. the request.user? or does the Survey object have a user attribute? in which case you would not need to pass user in and would just use survey.user etc.
django_survey\views.py
def SurveyDetail(request, id):
survey = Survey.objects.get(id=id)
category_items = Category.objects.filter(survey=survey)
categories = [c.name for c in category_items]
print 'categories for this survey:'
print categories
if request.method == 'POST':
form = ResponseForm(request.POST, survey=survey, user=request.user)
if form.is_valid():
response = form.save()
return HttpResponseRedirect("/confirm/%s" % response.interview_uuid)
else:
form = ResponseForm(survey=survey, user=request.user)
print form
In your view when you initialize your form you need to pass it the user (current user in this case)? similar to this form = ResponseForm(request.POST, survey=survey, user=request.user). Then in the __init__ of your form pop the user object user = kwargs.pop('user'). I believe that will resolve your issue.
this is my forms.py
CHOICES = []
class salDeptChartForm(forms.Form):
company = forms.CharField(max_length=2,label = 'Firma',help_text='A valid email address, please.')
date_validfrom = forms.DateField(label = 'Bu Tarihten',required=False)
date_validuntil = forms.DateField(label = 'Bu Tarihe Kadar',required=False)
saldept = forms.MultipleChoiceField(label = 'Satış Departmanları',choices=CHOICES, widget=forms.CheckboxSelectMultiple())
this is where I override the choices in my view.
form = salDeptChartForm(initial={'company':'01'})
saldeptlist = saleinstance.fetchSalDept()
form.fields['saldept'].choices = saldeptlist <this is where I override>
problem occurs when I select one of the options. form doesnt get validate.
Select a valid choice. * is not one of the available choices.
I think, even I override the choices in my view django still checks with previous choices itially I created. I get the correct html output tough.
How to overcome this?
thx
complete view code is there.
form initiates twice one for get and one for post, I dont know if its best either.
def salDept(request):
member_id = request.session['member_id']
saleinstance = sale(member_id)
chartinstance = charts(member_id)
if request.method == 'GET':
form = salDeptChartForm(initial={'company':'01'}) <first init>
saldeptlist = saleinstance.fetchSalDept() <its a list>
form.fields['saldept'].choices = saldeptlist <override choices>
print 'get worked'
return render(request, 'chart/sale/salDept.html',locals())
if request.method == 'POST':
form = salDeptChartForm(request.POST) <second init>
print 'post worked'
if form.is_valid(): <fails>
print 'valid'
company = form.cleaned_data['company']
vfr = form.cleaned_data['date_validfrom']
vun = form.cleaned_data['date_validuntil']
validfrom = formatDate(vfr)
validuntil = formatDate(vun)
selectedSalDepts = request.POST.getlist('saldept')
else:
print 'not valid'
print form.errors
resultdict = chartinstance.salesBySaldept(company,selectedSalDepts,validfrom, validuntil)
form = salDeptChartForm(initial={'company':company,'date_validfrom':request.POST['date_validfrom'], 'date_validuntil':request.POST['date_validuntil']})
domcache = 'true'
return render(request, 'chart/sale/salDept.html',locals())
Okay, you need override the init() of the form to do accomplish this.
class SomeForm(forms.Form):
email = forms.EmailField(label=(u'Email Address'))
users = forms.MultipleChoiceField(choices=[(x, x) for x in User.objects.all()]
)
def __init__(self, *args, **kwargs):
user = kwargs.pop('user', None)
super(SomeForm, self).__init__(*args, **kwargs)
self.fields['users'].choices = [(x, x) for x in User.objects.filter(name__contains='Patel')]
def clean(self):
return self.cleaned_datas
Here in line number (3) you can see that I have provided all the possible choices and then in the init I have filtered the choices, this is important because Django validates your submitted request from the former and displays the choices from the latter
Your validation fails because you only overwrite the choices on the GET method. You don't do anything for the POST, so as far as Django knows, no choice is valid for the POST. Adding the choices to POST should fix your problem.
I got a form as following:
class CourseAddForm(forms.ModelForm):
"""Add a new course"""
name = forms.CharField(label=_("Course Name"), max_length=100)
description = forms.Textarea()
course_no = forms.CharField(label=_("course Number"), max_length=15)
#Attach a form helper to this class
helper = FormHelper()
helper.form_id = "addcourse"
helper.form_class = "course"
#Add in a submit and reset button
submit = Submit("Add", "Add New Record")
helper.add_input(submit)
reset = Reset("Reset", "Reset")
helper.add_input(reset)
def clean(self):
"""
Override the default clean method to check whether this course has been already inputted.
"""
cleaned_data = self.cleaned_data
name = cleaned_data.get('name')
hic = cleaned_data.get('course_no')
try:
course=Course.objects.get(name=name)
except Course.DoesNotExist:
course=None
if course:
msg = u"Course name: %s has already exist." % name
self._errors['name'] = self.error_class([msg])
del cleaned_data['name']
return cleaned_data
else:
return self.cleaned_data
class Meta:
model = Course
As you can see I overwrote the clean method to check whether this course has already existed in the database when the user is trying to add it. This works fine for me.
However, when I want to add the same check for the form for editing, the problem happened. Because it is editing, so the record with same course name has already exist in the DB. Thus, the same check would throw error the course name has already exist. But I need to check the duplication in order to avoid the user updating the course name to another already existed course name.
I am thinking of checking the value of the course name to see if it is changed. If it has been changed, than I can do the same check as above. If it has not been changed, I don't need to do the check. But I don't know how can I obtain the origin data for editing.
Does anyone know how to do this in Django?
My view looks as following:
#login_required
#csrf_protect
#never_cache
#custom_permission_required('records.change_course', 'course')
def edit_course(request,course_id):
# See if the family exists:
try:
course = Course.objects.get(id=course_id)
except Course.DoesNotExist:
course = None
if course:
if request.method == 'GET':
form = CourseEditForm(instance=course)
return render_to_response('records/add.html',
{'form': form},
context_instance=RequestContext(request)
)
elif request.method == 'POST':
form = CourseEditForm(request.POST, instance=course)
if form.is_valid():
form.save()
return HttpResponseRedirect('/records/')
# form is not valid:
else:
error_message = "Please correct all values marked in red."
return render_to_response('records/edit.html',
{'form': form, 'error_message': error_message},
context_instance=RequestContext(request)
)
else:
error = "Course %s does not exist. Press the 'BACK' button on your browser." % (course)
return HttpResponseRedirect(reverse('DigitalRecords.views.error', args=(error,)))
Thank you.
I think you should just set unique=True on the Course.name field and let the framework handle that validation for you.
Update:
Since unique=True is not the right answer for your case, you can check this way:
def clean(self):
"""
Override the default clean method to check whether this course has
been already inputted.
"""
cleaned_data = self.cleaned_data
name = cleaned_data.get('name')
matching_courses = Course.objects.filter(name=name)
if self.instance:
matching_courses = matching_courses.exclude(pk=self.instance.pk)
if matching_courses.exists():
msg = u"Course name: %s has already exist." % name
raise ValidationError(msg)
else:
return self.cleaned_data
class Meta:
model = Course
As a side note, I've also changed your custom error handling to use a more standard ValidationError.
I believe excluding the current instance id from the results would solve the problem:
from django.db.models import Q
try:
qs = Course.objects.filter(name=self.cleaned_data.get('name'))
if self.instance.pk is not None:
qs = qs.filter(~Q(pk=self.instance.pk))
course = qs.get()
except Course.DoesNotExist:
course = None
However as dokkaebi pointed out, unique is really the better way to go with this, as this solution is vulnerable to race conditions. I'm not sure what your datamodel looks like but I suspect defining
class Meta:
unique_together = ('department', 'name')
should accomplish what you want.
I'm currently working on an application for user registration on a Django CMS site.
My registration form is working just fine and creates the user, plus adds my custom fields to my registration model.
What I'd like to know is how to update auth_user to contain the user's first and last name.
Currently the user is being created in this manner:
import newform
from betaregistration.newform import RegistrationFormZ
def user_created(sender, user, request, **kwargs):
form = RegistrationFormZ(request.POST)
data = newform.BetaProfile(user=user)
data.first_name = form.data["first_name"]
data.last_name = form.data["last_name"]
data.address = form.data["address"]
data.city = form.data["city"]
data.state = form.data["state"]
data.postal_code = form.data["postal_code"]
data.country = form.data["country"]
data.phone= form.data["phone"]
data.email = form.data["email"]
data.save()
# this was the solution
user.first_name = form_data['first_name']
user.last_name = form_data['last_name']
user.save()
# end solution
from registration.signals import user_registered
user_registered.connect(user_created)
Any help is appreciated. I'm using django-registration for my registration.
Since you are passing user into user_created could you simply modify things as the following:
data.first_name = user.first_name = form.data["first_name"]
data.last_name = user.last_name = form.data["last_name"]
....
user.save()
I may be missing something but if you really do have access to user in user_created that should work. You could also potentially do the same using request.user instead of user.
I have models.py
class Visit(Model):
reference_visit = models.ForeignKey('self',
help_text="Visit needs a refrence to Prior Visits",
null=True, blank=True)
show_prior_responses = models.BooleanField(default=False,
help_text="Show PriorResponses")
# has many field but i am making it short.
def __unicode__(self):
result = """Visit id:%s pt:%s""" % (self.id, self.patient.id)
return result
forms.py
class VisitSetupForm(Form):
list_visit_ids = ModelChoiceField(
queryset=Visit.objects.none(),
empty_label='Select Revisit ID',required=False)
show_prior_visit = ModelChoiceField(
queryset=User.objects.all(),
empty_label="Select User for Revisit",required = False)
has many but question is on list_visit_ids.
views.py
def setup(request):
"""
Allow an Admin user the ability to setup a patient & visit all at once.
"""
if request.user.is_superuser:
form_class = AdminVisitSetupForm
all_topics = True
else:
form_class = VisitSetupForm
all_topics = False
f = form_class()
# Get a list of topics for each report.
report_topics = {}
for r in Interview.objects.all():
report_topics[r.id] = [t['ad'] for t in r.topics.values('ad')]
data = {
'superuser':request.user.is_superuser,
'report_topics':simplejson.dumps(report_topics)
}
try:
request.user.reviewer
data['reviewer'] = True
except:
pass
if request.method == "POST":
f = form_class(request.POST)
if f.is_valid():
# Create the patient, generate a password, and send them on their way.
cd = f.cleaned_data
patient = None
if cd['revisit']:
# Check for an existing user first.
try:
patient = Patient.objects.get(username=cd['username'])
except Patient.DoesNotExist, e:
data['form'] = f
data['msg'] = 'There is no user with this username.'
return render_to_response('visit/setup.html', data, context_instance=RequestContext(request))
admin_user = get_user(request)
organization = None
if admin_user:
organization = admin_user.organization
if patient and not request.user.is_superuser:
# Make sure the patient they've selected is one of their own.
if patient.organization != organization:
return HttpResponseForbidden('You are not allowed to see this page.')
if not patient:
password = generate_password()
user = User.objects.create_user(cd['username'], cd['contact_email'], password)
user.first_name = cd['first_name']
user.last_name = cd['last_name']
user.save()
patient = Patient(
user=user,
username=user.username,
contact_phone=cd['contact_phone'],
date_of_birth=cd['date_of_birth'],
email=user.email,
first_name=user.first_name,
gender=cd['gender'],
last_name=user.last_name,
maiden_name=cd['maiden_name'],
organization=organization,
patient_type=cd['patient_type'],
security_answer=cd['security_answer'],
security_question=cd['security_question'],
)
patient.save()
# Send them an email.
t = loader.get_template('www/new_account.txt')
c = Context({
'password':'%s-%s-%s' % (password[:3], password[3:5], password[5:]),
'patient':patient
})
msg = t.render(c)
try:
send_mail(
'A request by your physician to do an online medical history before your appointment.',
msg,
'support#careprep.com',
[user.email]
)
except Exception, e:
log.error('Could not send email for new account %s because: [%s]' % (user.username, e))
request.session['password'] = password
# Create the Visit, too.
interview = cd['interview']
list_visit_ids = cd['list_visit_ids']
print list_visit_ids
visit = Visit(
reference_visit = cd['list_visit_ids'],
show_prior_responses = cd['show_prior_responses'],
patient=patient
)
if request.user.is_superuser:
topics = cd['topics']
else:
topics = set(list(interview.topics.all()) + list(cd['topics']))
reviewer_mode = cd.get('reviewer_mode') or patient.patient_type == 'Reviewer'
url, visit = initialize_visit(
request,
patient=patient,
starting_section=interview.starting_section,
visit_title='%s %s' % (patient, interview.title),
topics=topics,
reviewer_mode=reviewer_mode,
chief_complaint=cd['chief_complaint'],
location=cd['interview_site'],
reference_visit = cd['list_visit_ids'],
show_prior_responses = cd['show_prior_responses'],
)
next_url = "/visit/confirmation/%s/%s/?next=%s" % (patient.user.id, interview.id, url)
else:
v = Visit.objects.get(pk=request.POST['list_visit_ids'])
print v
return HttpResponseRedirect(next_url)
# all the fields that are not given pls ignore.
The template is fine.
Now watch forms.py when i do list_visit_ids = ModelChoiceField(queryset=Visit.objects.all(), empty_label='Select Revisit ID',required=False) It works perfectly fine on my local machine.But on my server it has around 6000 visit objects so this page hangs or i should say keep on loading.
So initially i changed it to list_visit_ids = ModelChoiceField(queryset=Visit.objects.none(), empty_label='Select Revisit ID',required=False)
Now i know that by this the form becomes invalid and should go to the else part Now my question how do i make reference_visit=cd['list_visit_ids'] in else (form is invalid)
case save().How do i override the none() attribute.
Thanks in advance i will really appreciate.
If your goal is to save your html page load by removing the 6000 choices (which I've done too: 10000+ <option> fields wrapped by misc html will absolutely choke a page), you shouldn't be using a ChoiceField at all. By setting queryset=Visit.objects.none() you're allowing zero choices and nothing passed in will validate.
You either show 6000 select item drop downs, radio boxes, etc., or find a way to /not/ have a giant select drop down (such as a hidden input or charfield), not fake around a ModelChoiceField who's main purpose is to populate that select drop down and validate.
In short: don't use a ModelChoiceField if you're not going to be using the html choices generated by it. Use something else and do the validation / model pulling yourself via the clean_FOO methods.
class MyForm(forms.Form):
my_input = forms.CharField()
def clean_my_input(self):
input = self.cleaned_data.get('my_input')
try:
return MyModel.objects.get(pk=input) # add a filter here if you want
# (whatever filters you were using in the queryset argument)
except MyModel.DoesNotExist:
raise forms.ValidationError("Doesn't exist / is invalid")
return input