Multiple Form with Single Submit Button - django

I'm currently working with django project. I had to filter the data store on the database based on the user input on form (at template) as looked below.
On form user either enter value or leave it blank. So what I have to do is first find the (valid) user input and then fire appropriate query to display data as user input in the form. So final result should be displayed on table at template.
As I'm new to django, how should I have to pass the data and fire query to represent data at multiple field. As help or link related to these type problem are expected. ( I just able to filter from the database with only one form and had no concept to solve this.)
Model of my temp project is as below.
class exReporter(models.Model):
first_name = models.CharField(max_length=30)
last_name = models.CharField(max_length=30)
email = models.EmailField()
gender = models.CharField(max_length=1)
age = models.IntegerField()
label = models.IntegerField()

There are a number of approaches you can take, but here is one solution you can use that involves chaining together the appropriate filters based on the form's posted data:
*Note: To conform to Python's naming convention, rename exReporter class to ExReporter.
# views.py
def process_ex_reporter_form(request):
if request.method == "POST":
# ExReporterForm implementation details not included.
ex_reporter_form = ExReporterForm(request.POST)
if ex_reporter_form.is_valid():
# If form field has no data, cleaned data should be None.
gender = ex_reporter_form.cleaned_data['gender']
age_start = ex_reporter_form.cleaned_data['age_start']
age_end = ex_reporter_form.cleaned_data['age_end']
aggregation_group = ex_reporter_form.cleaned_data['aggregation_group']
aggregation_id = ex_reporter_form.cleaned_data['aggregation_id']
ex_reporters = ExReporter.objects.get_ex_reporters(gender, age_start,
age_end, aggregation_group, aggregation_id)
else:
# Pass back form for correction.
pass
else:
# Pass new form to user.
pass
# models.py
class ExReporterManager(models.Manager):
def get_ex_reporters(self, gender, age_start, age_end, aggregation_group,
aggregation_id):
ex_reporters = super(ExReporterManager, self).get_query_set().all()
# Even though the filters are being applied in separate statements,
# database will only be hit once.
if ex_reporters:
if gender:
ex_reporters = ex_reporters.filter(gender=gender)
if age_start:
ex_reporters = ex_reporters.filter(age__gt=age_start)
if age_end:
ex_reporters = ex_reporters.filter(age__lt=age_end)
# Apply further filter logic for aggregation types supported.
return ex_reporters

Related

Django - Saving data without using input fields/forms

I am a novice, apologies if this question seems silly. I need to save some data into MySQL database. There are no input fields. The user should click a button, and a table is updated. The data to be saved is two foreign keys and a PK.
Here is my model
class Bids(models.Model):
id=models.AutoField(primary_key=True)
userid = models.ForeignKey(Writer, on_delete=models.DO_NOTHING, related_name='userid ')
orderid = models.ForeignKey(Orders, on_delete=models.DO_NOTHING, related_name='orderids')
biddatetime=models.DateTimeField(auto_now_add=True)
I have tried writing several functions to save these fields into table bids but no joy so far. Hers's a sample.
def saveBid(request):
if request.method!= "POST":
return HttpResponse("Action Not Allowed")
else:
biddatetime=request.POST.get('biddatetime')
bids= Bids(biddatetime=biddatetime)
order=Orders(id=id)
user= CustomUser()
user.save()
bids.save()
Pls assist
I would try sending a POST request to saveBid using Postman and what error you're getting. Post the response from postman here for more help.
It could be that
biddatetime is a string and not a datetime.
On row order=Orders(id=id) you have no variable named id in your code, this will raise error.
In your model Bids the fields userid and orderid do not allow null and blank.
You can use strptime() to convert biddatetime to datetime object.
Try something like that:
from datetime import datetime
def saveBid(request):
if request.method != "POST":
return HttpResponse("Action Not Allowed")
else:
query = request.POST
# See Format Codes - link below
biddatetime = datetime.strptime(query.get('biddatetime'), "%Y-%m-%d")
# get Order
order = Orders.objects.get(id=query.get("order_id")
# create CustomUser
user = CustomUser.objects.create(username="username")
# create Bids
bids = Bids.objects.create(biddatetime=biddatetime, userid=user, orderid=order)
create() method:
create(**kwargs)
A convenience method for creating an object and saving it all in one
step. Thus:
p = Person.objects.create(first_name="Bruce", last_name="Springsteen")
Linkt to Format Codes.
See also Creating objects.
Any reason why you are not using django Form or ModelForm?
class BidForm(forms.Form):
biddatetime = forms.DateTimeField()
.... // other fields
#require_POST
def saveBid(request):
form = BidForm(request.POST)
if form.is_valid():
biddatetime = form.cleaned_data.get('biddatetime')
... // do same for similar fields.
...// after user save
user.refresh_from_db() // post insert you will get the id value for the row
bids = Bids(
biddatetime=biddatetime,
userid=user.userid,
orderid=order.orderids)
bids.save()
I am assuming you are using the id value for user after save if thats not the case you can ignore it.

Django validate data when updating model with primary key

I am having trouble with updating fields of a model instance. The model is as follows:
class commonInfo(models.Model):
mothers_id = models.IntegerField(primary_key=True)
date = models.DateField()
data_collector = models.CharField(max_length=50)
Essentially, I just want to do this, but it won't work because commonInfo has a user defined primary key
commonInfo_form(request.POST or None).is_valid()
Since I am updating, I am overriding date and data_collector, but not mothers_id. So I would want to do something like this, but this specific code is not working
obj = commonInfo.objects.get(pk=commonInfo_id)
form = commonInfo_form(request.POST)
date = form.cleaned_data['data_collector'] #this line is not working
data_collector = form.cleaned_data['data_collector'] #this line is not working
obj.update(**{'date':date, 'data_collector':data_collector})
any ideas? I feel like it is just those two lines that I need to fix. Or if there is a more pythonic way or built method in Django?
Just validate with isinstance. so like,
if isinstance(request.POST['date'], datetime.date) and isinstance(request.POST['data_collector'], str):
# you might have to use getattr for request.POST here, I'm not sure
# and request.POST['date'] would have to be converted from a string to datetime.date I think
date = request.POST['date']
data_collector = request.POST['data_collector']
obj.update(**{'date':date, 'data_collector':data_collector})
The process for adding a record from a form is different from updating an existing instance. All you need to do differently is indicate which instance to bind the form to when you create it, ex:
obj = commonInfo.objects.get(pk=commonInfo_id)
form = commonInfo_form(request.POST, instance=obj)

Django Forms - Relating Objects (Model Formsets?)

Say I have something like this:
class Product(models.Model)
name = models.CharField()
description models.TextField()
class Image(models.Model)
product = models.ForeignKey(Product, null=True, blank=True, related_name="images")
image = models.ImageField()
Suppose I'm in a form for creating a Product and in this form there is a section that allows you to upload images. These images are uploaded asynchronously. How can I have it so that:
On creation:
Product is created and images are related to it.
On editing:
Product is fetched, related Images are fetched. Product is edited, Images are edited.
Currently, I have a view that does both jobs of creating or editing Products. How can I accomplish the image related part of this product form? Model Formsets?
Edit:
#login_required
def create_or_edit_product(request, product_id=None):
# Redirect user if he has no account associated to him
try:
account = Account.objects.get(membership__user=request.user)
except:
login_url = reverse('login') + ('?next=%s') % request.path
return HttpResponseRedirect(login_url)
# Get product if product id passed
product_instance = None
if product_id is not None:
product_instance = get_object_or_404(product, id=product_id)
# Get related pictures if product exists. Get picture values (dictionary list, used for initial formset data) if pictures found.
pictures = product.pictures.all() if product_instance is not None else None
pictures_values = pictures.values() if pictures else []
PictureFormSet = formset_factory(PictureForm, formset=BasePictureFormSet, extra=0, can_delete=False, max_num=40)
if request.method == "POST":
product_form = productForm(request.POST, prefix='product', instance=product_instance)
picture_formset = PictureFormSet(request.POST, prefix='pictures', initial=pictures_values)
# If forms are valid
if product_form.is_valid() and picture_formset.is_valid():
try:
# Add account to product and save
product = product_form.save(commit=False)
if product_instance is None:
product.account = account
product.save()
# Remove picture-product relationships of current pictures
if pictures is not None:
pictures.update(product=None)
# Update each picture with the product and corresponding sort order. (The field 'id' is a picture object. The form converts the passed picture id to a picture object)
for index, form in enumerate(picture_formset):
picture = form.cleaned_data['id']
Picture.objects.filter(id=picture.id).update(product=product, sort_order=index)
except Exception, e:
# Rollback changes - something went wrong
transaction.rollback()
print "Transaction Rollback: %s" % e
else:
# Commit changes and redirect to product edit page
transaction.commit()
return HttpResponseRedirect('product_edit', product_id=product.id)
else:
product_form = productForm(prefix='product', instance=product_instance)
picture_formset = PictureFormSet(prefix='pictures')
# TODO: change add template to an add/edit one
return render_to_response('products/add.html', {
'product_form' : product_form,
'picture_formset' : picture_formset,
'pictures' : pictures,
},
context_instance=RequestContext(request))
I'm new to Python and Django but that is my somewhat working view. (I've only tried adding a new product so far)
The form that the user sees has thumbnails with hidden inputs containing each picture id. In the case that the form fails, I'd like to re display thumbnails along with the hidden inputs (which are already kept). To make that work, I'm guessing I'd have to query the picture ids, but the form is not valid so how would I go about doing that? (if that is even the right path to go about)
What do you think?
#Aamir Adnan is right that Model Formsets are an elegant way to deal with this.
Here's a full fledge example that is similar to what you need - http://stellarchariot.com/blog/2011/02/dynamically-add-form-to-formset-using-javascript-and-django/
As in the example, because the end user may need to add additional "images" (in your case) which are related to the product, you will need some javascript logic to handle the dynamic addition of forms so that the user can arbitrarily add and upload images on save.

Django form cache or reload issue

I have a page to insert value to db.
After inserting am loading another page with a drop down listing the db values
But the inserted value is not listing in dropdown
The problem is not with transaction/commit etc. The query to retrieve the data for the drop down in second form is correct.
Form1(first page)
class Organization(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.'})
Form2(Second page)
class User(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
but when i tried to bind the choices in view it is working
working code
*view*
def user()
template = get_template('AddUser.html')
form = AddUser()
orgUnitList = getOrgUnitList()
del objAdminUIDA
form.fields['orgUnit'].widget.choices=orgUnitList
variables = RequestContext(request,{'form':form})
output = template.render(variables)
del form
return HttpResponse(output)
But i cant give the dropdown choice in view i want to give choices in form.i need a solution for form2
Firstly, the orgList is evaluated in form definition, that's why choices don't change. You should place getOrgUnitList in form's __init__ (or in some other method).
Secondly, you don't pass any data to the form, may be you want
form = AddUser(request.POST or None)

Django get_FIELD_display

I am trying to access data.get_age_display in my email template. I can't seem to get the display of this. I am not sure what I am doing wrong, I've using get_FIELD_display numerous times before but passed as context to a normal template. Is there something different with forms?
class RequestForm(forms.Form):
ADULT = 1
SENIOR = 2
STUDENT = 3
AGE_GROUP = (
(ADULT, 'Adult'),
(SENIOR, 'Senior'),
(STUDENT, 'Student'),
)
name = forms.CharField(max_length=255)
phone = forms.CharField(max_length=15)
age = forms.ChoiceField(choices=AGE_GROUP)
details = forms.CharField(widget=forms.Textarea())
def save(self):
order = Order(
name = self.cleaned_data['name'],
phone = self.cleaned_data['phone'],
age = self.cleaned_data['age'],
details = self.cleaned_data['details'],
)
order.save()
template = loader.get_template('request_email.txt')
# send over the order object in an email extracted so they can handle the ticket order
context = Context({
'data': order,
})
#import pdb; pdb.set_trace()
email_subject = 'Request Tickets'
mail_managers(email_subject, template.render(context))
in my request_email.txt all I am doing is {{ data.get_age_display }} any ideas?
Jeff
You haven't shown the code for the Order model that you're creating. Are you sure that the age field on the model has choices set?
Any reason you're not using a ModelForm? You're creating an Order object within the form's save() method, but not returning it. A modelform would do that for you, as well as removing the need to redeclare the fields for the form.
I know this is coming WAAAAAY later than the question being posted but here's my answer for completeness and anyone else who might benefit from it :-)
I'm going to assume that in AGE_GROUP, ADULT, SENIOR and STUDENT are integers. Your form cleaning will NOT automatically clean the string contained in the POST and return an integer. So in this code:
context = Context({
'data': order,
})
you would think order.age is referring to an integer but that is, in fact, incorrect. It's burned me a few times before because this will correctly save the integer to the physical table, but the order instance still has the string representation of the age field.
You could do one of two things:
1. Clean this in the field:
clean_age(self):
return int(self.cleaned_data['age'])
or create a new field type:
def MyChoiceField(forms.ChoiceField):
def clean(self, value):
if not value:
if self.required:
raise forms.ValidationError(self.error_messages['required'])
return None
else:
return None
return int(value)
link that to the form field:
age = MyChoiceField(choices=AGE_GROUP)
and then you'll be able to apply this logic to any other such choice field in future. Personally, I find the latter approach the best one and I stick all my custom field types into a form_utils file so that I can use them everywhere. Another gotcha is that forms.charField doesn't automatically strip the entered text and you can use this approach to fix that too.