I have a form that enters data to db.
I have another form with a drop down field that uses the data entered by the first form.
So when I submit data from the first form,the db is updated properly.
But when I load the second form the drop down is not updated with the latest data.
Steps followed for debugging
The problem is not with transaction/commit etc.
The query to retrieve the data for the drop down in second form is correct.
The problem is not with view cache either(cos we don't have any cache middleware)
I also tried the cache decorators like #never_cahce,#cache_control etc
I tried giving a print statement in second form.
I believe that the problem is with form cache.
Every django form is loaded only once,ie. while loading the first page of the site.
Afterwards the form is loaded from this cache.
First page
form
class AddOrganization(forms.Form):
orgList = getOrgUnitList()
orgUnit = forms.CharField(label=u'Organization Name',
max_length=50,
error_messages={'required':'Organization name is required field.'})
parentOrg= forms.ChoiceField(label=u'Parent Organization',
choices=[(u'Select',u'Select')]+orgList,
error_messages={'required':'Organization name is required field.'})
Second page
form
class AddUser(forms.Form):
orgUnitList = getOrgUnitList()
email = forms.EmailField(label=u'Email',
max_length=50,
error_messages={'required':'Email is required field'})
orgUnit = forms.ChoiceField(label=u'Organizational Unit',
choices=orgUnitList,
error_messages={'required':'Organizational unit is required field'})
Query
def getOrgUnitList():
orgUnitList = list(OrganizationUnit.objects.values_list('OrgUnitID','OrgUnitName').order_by('OrgUnitName'))
return orgUnitList
EDIT
Everything is just fine if I use modelforms.Why So?
The issue is the declaration of orgUnitList as a class property in the form. This means it's called once, when the form is originally defined. So no new elements will be seen, until the server process is restarted.
One way to fix this would be to call the getOrgUnitList function inside the __init__ method of the form:
class AddOrganization(forms.Form):
...
def __init__(self, *args, **kwargs):
super(AddOrganizationForm, self).__init__(*args, **kwargs)
self.fields['orgUnit'].choices = getOrgUnitList()
Alternatively, you should look into using ModelChoiceField for orgUnit, as it deals with this sort of thing automatically.
Related
I have a form that takes in user data like bio, profile picture, gender, etc. and upon submission either creates this new row about the user or updates the existing row. This will only work if the user uploads an image. If no image is uploaded for the profile picture, then the form doesn't submit. How can I make it so that if the user didn't upload a profile picture, then it'll keep the user's previous profile picture and still submit?
Here's my code:
class ProfileSettings(UpdateView):
model = Profile
template_name = 'blog/settings.html'
form_class = ProfileForm
success_url = reverse_lazy('blog:settings')
def post(self, request, *args, **kwargs):
form = self.form_class(request.POST, request.FILES)
if form.is_valid():
bio = form.cleaned_data['bio']
gender = form.cleaned_data['gender']
avatar = form.cleaned_data['avatar']
Profile.objects.update_or_create(user=self.request.user, defaults={'avatar':avatar, 'bio':bio, 'gender':gender})
return HttpResponseRedirect(self.success_url)
I'll give you the quick and dirty - 3 places to solve this:
Javascript - make the form aware of what fields are required and pre-fill if the username already exists (out of scope from your question but just throwing it out there)
In the API endpoint (this seems to be the approach you are going for)
In your model (implement a custom save function that looks to see if new, and compare initial value to subsequent value)
I'll dump options 1 and 3 because they aren't pertinent to your question as asked. I'm assuming user is unique per profile. And I'm assuming that currently the field avatar is required. If you set that to not required then the form post should allow a null value for avatar - How to make FileField in django optional?. You may be thinking, but I don't want that field to be possibly blank - you could always enforce that the first time the post is made that the field is set via the API endpoint itself. If you made that field optional then the form would post but you may want to be more explicit with .update_or_create by actually checking to see if the object already exists and if so require the field or if not confirm that field is set.
I have a form with some fields. I want to display all fields in form except two. However one field data needs to be displayed.
I am able to get the form but not able to retrieve the data from DB to display.
Model.py
class Company(models.Model):
STATUS_CHOICES=(
('service','service'),
('product','product'),
)
user=models.OneToOneField(settings.AUTH_USER_MODEL)
company_name=models.CharField(max_length=250)
company_address=models.CharField(max_length=250)
Company_telephone=models.CharField(max_length=250,blank=True)
company_email=models.CharField(max_length=250,blank=True)
company_website=models.CharField(max_length=250,blank=True)
VAT=models.CharField(max_length=250,blank=True)
Service_Tax=models.CharField(max_length=250,blank=True)
company_PAN=models.CharField(max_length=250,blank=True)
company_bankdetails=models.CharField(max_length=250,blank=True)
invoice_type=models.CharField(max_length=250,choices=STATUS_CHOICES,default='service')
def __str__(self):
return 'self.user.company_name'
forms.py
class companyeditform(forms.ModelForm):
class Meta:
model=Company
exclude = ('user','company_name',)
views.py
#login_required
def companyadd(request):
if request.method == 'POST':
company_form=companyeditform(instance=request.user.company,data=request.POST)
if company_form.is_valid():
new_form=company_form.save(commit=False)
new_form.save()
return render(request,'account/dashboard.html',{'section':'addcompany'})
else:
company_form=companyeditform(instance=request.user.company)
company_details=Company.objects.get(user=request.user.company)
return render(request,'account/company.html',{'company_form':company_form})
When form is displayed everything works as planned. However not getting company_name.
Using this query to get company name.
company_details=Company.objects.get(user=request.user.company)
Django gives following error:
Cannot query "self.user.company_name": Must be "User" instance.
In this query company_details=Company.objects.get(user=request.user.company) you are trying to get the company of a particular user. But in the statement, you are comparing user=request.user.company, both are two different types (User is the authuser model and request.user.company is the Company model). You cannot do this in the query.
company_details=Company.objects.get(user=request.user) This statement will solve the issue. And also you can do
company_details=request.user.company because the association is OneToOne.
The reason you are getting that error, is because you are trying to fetch a Company by filtering it out the company that matches the current user, but you are passing in the actual company object:
company_details=Company.objects.get(user=request.user.company)
# ^^^^^^^
You can fix the line, by doing this:
company_details=Company.objects.get(user=request.user)
But you already have the correct object, in request.user.company, you don't need to fetch it again, simply:
company_details = request.user.company
print(company_details.company_name)
In fact, since you are using the render shortcut, you don't even need to do this step, as the request object will be available in your template, so all you need to do really is:
Company: {{ request.user.company.company_name }}
Finally, you should always redirect after a POST request (see this article on wikipedia for the details).
request.user might be a class like AnonymousUser. Do some extra processing to ensure that request.user is the type provided by django.contrib.auth.get_user_model().
I am having problems saving a MultipleChoiceField on my test server (with Apache). However, it works on my local machine (django server).
In a form, I have checkboxes that I can check and uncheck. When I click on a save button, the data related to the checkboxes are saved in the database and the form is reloaded and the checkboxes updated.
However, this is how it works in local but not on the test server. On the test server, when I click on the save button, it just reloads the form, nothing is saved, nothing is changed.
Here is the code:
class Participant(models.Model):
databases = models.ManyToManyField(Advertiser, null=True, blank=True, through='ShareDataToBrands')
#property
def share_to_brands_list(self):
brands=[]
for brand in ShareDataToBrands.objects.all():
brands.append((brand.advertiser.id, brand.advertiser.brand_name, brand.share_data))
return brands
class ShareDataToBrandsForm(forms.ModelForm):
class Meta:
model = models.Participant
fields = ('databases', )
databases=forms.MultipleChoiceField(widget=forms.CheckboxSelectMultiple, required=False)
def save(self, *args, **kwargs):
instance=super(ShareDataToBrandsForm, self).save(commit=False)
#list of brands to share data with
share_list=map(int, self.cleaned_data.get("databases"))
participant=self.instance
for share_data_instance in models.ShareDataToBrands.objects.filter(participant=participant):
if share_data_instance.advertiser.id in share_list:
share_data=True
else:
share_data=False
#update record
share_data_instance.share_data=share_data
share_data_instance.save()
return instance
What could possibly be wrong?
EDIT :
When I check the log, I see that the program never enters in the for loop! However, I have records in the database matching the filter.
I have found the solution, looping through theShareDataToBrands instances related to the participant instead of all the ShareDataToBrands instances.
In the share_to_brands_list property, have changed the following line:
for brand in ShareDataToBrands.objects.all():
with
for brand in ShareDataToBrands.objects.filter(participant=self):
I have a System that can have one or more Models. I have modeled this relationship in the database with a manytomany field. The code below is for editing the system and its associated methods in a single form.
Adding a new method by filling out its form and pressing submit works only the first time. If I then make a small change and submit again, I get the following message (generated by the code below):
METHODFORMSET.ERRORS: [{}, {'name': [u'Method with this Name already exists.']}]
This is caused by the fact that the name field is unique, but it should have updated, not created a new record, even though I am using the POST data to generate the methodformset instance...
Note that this behaviour only applies to the last appended method instance, not to ones that were already present in the table.
Here is the relevant code, can anyone let me know what I am doing wrong?
def sysedit(request, sys_id):
system = System.objects.get(id=sys_id)
MethodFormSet = modelformset_factory(Method, form=MethodForm)
post = None
if request.POST:
post = request.POST.copy()
if 'add_method' in request.POST:
post['method-TOTAL_FORMS'] = repr(int(
post['method-TOTAL_FORMS'])+ 1)
systemform = SystemForm(data=post, instance=system)
methodformset = MethodFormSet(data=post, prefix='method',
queryset=Method.objects.filter(id__in=system.method.all()))
if methodformset.is_valid():
mfs = methodformset.save()
print 'SAVED-method', mfs
for mf in mfs:
if systemform.is_valid():
sp = systemform.save(mf)
print 'SYSTEM', sp
else:
print 'SYSFORMSET.ERRORS:', systemform.errors
else:
print 'METHODFORMSET.ERRORS:', methodformset.errors
return render_to_response('sysedit.html',
{'systemform': systemform,
'methodformset': methodformset,
'system': system},
context_instance=RequestContext(request))
class System(models.Model):
method = models.ManyToManyField(Method)
...
class Method(models.Model):
name = models.CharField(unique=True)
...
class MethodForm(ModelForm):
class Meta:
model = Method
class SystemForm(ModelForm):
def save(self, new_method=None, commit=True, *args, **kwargs):
m = super(SystemForm, self).save(commit=False, *args, **kwargs)
if new_method:
m.method.add(new_method)
if commit:
m.save()
return m
class Meta:
model = System
exclude = ('method')
[EDIT after Sergzach's answer]:
The problem is not how to deal with the Method with this name already exists error, but to prevent that from occurring in the first place. I think the actual problem may have something to do with the way modelformsets deal with new forms. Somehow it looks like it always tries to create a new instance for the last formset, regardless of whether it already exits.
So if I do not add a new formset after the last one was appended, the modelformset will try to re-create the last one (even though it was just created on the previous submit).
The initial situation is that I have 1 valid Method instance and 1 new unbound instance in the methodformset. I then fill out the form and hit save, which validates both Methods and binds the 2nd one, which is then saved to the table.
So far all is well, but if I then hit save the 2nd time the error occurs. Maybe this has to do with the fact that method-TOTAL_FORMS=2 and method-INITIAL_FORMS=1. Could it be that this causes modelformset to force a create on the 2nd Method?
Can anyone confirm/deny this?
[Edit after a weekend of not looking at the code]:
The problem is caused by the fact that I am saving the forms in the view and after saving, I am sending the original methodformset instance (from before the save) to the template. The problem can be solved by re-instantiating modelformset after the save, using the queryset and NOT the POST data.
So the general rule to prevent errors like this, is either to go to a different page after a save (avoid it altogether), or use the above solution.
Before I post this as THE solution, I need to do more testing.
You can validate each form when saving a formset. I have created a simple example (similar to your code) and it works well for me. It creates new objects if there is no object with a such name otherwise it edits an existing object.
You need a form to edit your model objects:
class EditMethodForm( forms.ModelForm ):
class Meta:
model = Method
exclude = ( 'name', )
Then instead of methodformset.is_valid() you do the next:
for methodform in methodformset:
try:
instance = Method.objects.get( name = request.POST[ 'name' ] )
except Method.DoesNotExist:
methodform.save()
else:
editmethodform = EditMethodForm( request.POST, instance = instance )
if editmethodform.is_valid():
editmethodform.save()
There are some additional features in your code. I show the working principle. Is it enough to understand the solution?
I have solved the problem by re-instantiating modelformset after the save (see edit at the bottom of the question)
I have the following model in a project:
class CarAssignment(models.Model):
leg = models.ForeignKey(Leg, null=True, blank=True)
driver = models.ForeignKey(Driver, null=True, blank=True)
riders = models.ManyToManyField(Rider, null=True, blank=True)
I'm trying to create a page with a table where an admin user can edit the driver and riders items for all of the car assignments in a specific group. I can't use a formset because I also need to add data from another model, and because the choices in each list come from a different subset of riders and drivers, those that belong to a specific group. Driver is a select list and Riders is a multiselect list.
I have successfully built the page and table to represent the data, with correctly working drop-down lists. However, I can't figure out how to save the items. Here's a specific problem scenario:
for row 1 in the table (representing CarAssignment object #1), I pick a new driver from the list in the select dropdown. I'm assigning custom name tags in the html to identify them, as follows:
<option selected name="select.driver.{{ car_assignment.id }}">{{ driver }}</option>
Then in the view, I'm trying to see if it matches anything, as follows:
for car_assignment in CarAssignment.objects.filter(leg__in=legs):
driver_tag = "select.driver." + str(car_assignment.id)
if driver_tag in request.POST:
car_assignment.driver = driver
car_assignment.save()
The problem is that when it finds a matching driver tag, all it returns is a string of the First Name and Last Name. I can't do the car_assignment.driver = driver because it's trying to equate a driver object to a string.
I'm not sure how to do this. I guess I could parse out the first and last names and try to match them against the Driver model to get the Driver objects I need, but that seems inefficient and possibly error-prone.
Any help is appreciated (I'm a programming and Django newbie). Thanks.
I'm not sure where your that driver field is coming from, but if it's a String, you could then do another query to get the driver object like so:
for car_assignment in CarAssignment.objects.filter(leg__in=legs):
driver_tag = "select.driver." + str(car_assignment.id)
if driver_tag in request.POST:
# Get the driver object
driver = Driver.objects.filter(name=driver_name)[0]
car_assignment.driver = driver
car_assignment.save()
I'd also highly recommend that you read the Django tutorial Working with forms. The logic that you're writing in the view should be moved to a form object. Your view can then instantiate the form and pass the post data. The form can then be validated and will handle saving your new objects. This is the best way to do this.
The updated flow for your view would be simplified to something like this:
def my_view(request):
if request.method == 'POST':
form = MyForm(request.POST)
if form.is_valid():
form.save()
# Do something here like redirect the user
# to acknowledge that the form was submitted successfully.
else:
form = MyForm()
return render_to_response('form.html', {
'form': form,
})
By passing the form to your template you can generate the form doing nothing but this:
{{ form.as_ul }}