How to consult data with django forms? - python

I'm playing with Django and I have a situation where I have a form and the user define a two dates, like when we need to book a flight, departure date and return date. In my forms.py I have a Form class with two fields for dates
class MyForm(forms.Form):
dateOne = forms.DateTimeField()
dateTwo = forms.DateTimeField()
I need to retrieve objects from a model by passing the dates defined by the user. In my template I'm using get action, on form html tag, but I'm stuck on how I can make this consult and bring the objects to the template.
If I need create a logic on my views.py or in my forms.py. Does anyone know some thing to me start this task?

You're going to want to create an additional view that accepts an AJAX request and sends back a JSON response with the objects you're looking for. So if the user enters two dates you POST/GET those dates to the backend and then your view looks something like this:
class SomeView(View):
def get(self, request, *args, **kwargs):
object_list = YourObject.objects.filter(date__range=[start_date, end_date])
# Do something to your object_list to make it a JSON serializable list
return JsonResponse({'object_list': object_list})

Related

Safely store data from GET request - Django

Alright,
Let's say we need to create a website with Django where people can book a lodge for the weekends.
We add a search form on the homepage where people can fill in the check-in and check-out date
to filter for all available lodges.
We use a generic Listview to create an overview of all the lodges and we overwrite the queryset to grab the search parameters from the GET request to create a filtered view.
views.py
class ListingsView(ListView):
"""Return all listings"""
model = Listing
template_name = 'orders/listings.html'
def get_queryset(self):
"""
Visitors can either visit the page with- or without a search query
appended to the url. They can either use the form to perform a search
or supply an url with the appropriate parameters.
"""
# Get the start and end dates from the url
check_in = self.request.GET.get('check_in')
check_out = self.request.GET.get('check_out')
queryset = Calendar.objects.filter(
date__gte=check_in,
date__lt=check_out,
is_available=True
)
return queryset
Now this code is simplified for readability, but what I would like to do, is store the check-in and check-out date people are searching for.
Updated views.py
class ListingsView(ListView):
"""Return all listings"""
model = Listing
template_name = 'orders/listings.html'
def get_queryset(self):
"""
Visitors can either visit the page with- or without a search query
appended to the url. They can either use the form to perform a search
or supply an url with the appropriate parameters.
"""
# Get the start and end dates from the url
check_in = self.request.GET.get('check_in')
check_out = self.request.GET.get('check_out')
queryset = Calendar.objects.filter(
date__gte=check_in,
date__lt=check_out,
is_available=True
)
Statistics.objects.create(
check_in=check_in,
check_out=check_out
)
return queryset
We created a "Statistics" model to store all dates people are looking for.
We essentially add data to a model by using a GET request and I'm wondering if this is the right way of doing things? Aren't we creating any vulnerabilities?
The search form uses hidden text inputs, so there's always the possibility of not knowing what data is coming in. Is cleaning or checking the datatype from these input enough, or will this always be in string format?
Any ideas?
Greetz,

How to access object count in template using model manager?

I have a model which creates Memo objects. I would like to use a custom Model Manager's posted method to return the total number of Memo objects - then use this number within a template. I am trying to keep as much of my code as possible within my Models and Model Managers and less within my Views as I read that this was a best practice in 'Two Scoops of Django'.
In the shell I can get the number of memos as such:
>>> from memos.models import Memo
>>> Memo.objects.all()
<QuerySet [<Memo: Test Memo 2>, <Memo: Test Memo 1>]>
>>> Memo.objects.all().count()
2
This is what my Model and Model Manager look like:
class MemoManager(models.Manager):
use_for_related_fields = True
def posted(self):
return self.count()
class Memo(models.Model):
title = models.CharField(max_length=100)
content = models.TextField()
date_time = models.DateTimeField(default=timezone.now)
author = models.ForeignKey(User, on_delete=models.CASCADE)
objects = MemoManager()
def __str__(self):
return self.title
def get_absolute_url(self):
return reverse('memos-detail', kwargs={'pk': self.pk})
I know this is clearly the wrong way to do it but I have confused myself here. So how do I use my Model Manager to get the count of objects and use it in a template like: {{ objects.all.count }}?
P.S. I see other posts that show how to do this within the view but as stated I am trying not to use the view. Is using the view required? I also understand my posted method is written incorrectly.
I'm sorry but you have misinterpreted what was written in TSD. The Lean View Fat Model is meant to keep code which pertains to 'business logic' out of the views, and certain model specific things. A request should be handled by a view. So when you want to load a template, you must first have a GET request to your app.
A view function should be written such that Validation of POST data or the Creation of a new object in DB or Querying/Filtering for GET requests should be handled in the corresponding serializer/model/model manager.
What should be happening while you want to load your template.
Have a url for the template that you have created and a view function mapped for it
In the view function you should render said template and pass the necessary data inside the context.
To keep in line with the Lean View Fat Model style, if you want to get a Queryset of of Memo's but only those which have their is_deleted fields set to False, you can overwrite the model manager get_queryset() method for Memo model.
If you want to create a new Memo with a POST request, you can handle
the creation using a ModelForm!
Hope this clears things up!
EDIT:
How to pass a context to a template, in your case the memo count.
def random_memo_view(request):
context = {'memo_count': Memo.posted()}
return render(request, 'template.html', context=context)
RE-EDIT
I just checked that you were using DetailView. In this case follow this from the django docs.
Class Based Views: Adding Extra Context

Serving multiple templates from a single view (or should I use multiple views?)

Related to this post, I want to populate multiple HTML pages from a single Django view. The difference between this and the link I just mentioned is, I don't want it to be programmatically based. I have links on my template like "Reports" and other company-specific categories. If the user clicks on the Reports link, I want to take them to a new page that will show them the reports. This data is all interrelated, so I initially assumed I would/should be using the same view for all of it. As I started to write this post though, I started wondering if I should in fact use separate views for all of the pages. There shouldn't be more than 3-4 pages total, depending on how I want to split up the categories.
So TL;DR: Should I use separate views for each HTML page in my template, or should/could I use a single view to populate all of the various pages on the site, even if most of the data comes from the same sources?
A possible solution using class based Views is to create a base view class that will collect the common context data and then extend it as necessary for the specific data and template. Actually the base class does not have to be an extension of View, a ContextMixinextension is sufficient
The base class should look like this:
class BaseContextMixin(ContextMixin):
def get_context_data(self, **kwargs):
context_data = super(BaseContextMixin, self).get_context_data(**kwargs)
common_data_1 = ...
context_data["common_key_1"] = common_data_1
common_data_2 = ...
context_data["common_key_2"] = common_data_2
...
return context_data
the views then can be implemented as follows:
class MyFirstView(TemplateView, BaseContextMixin):
template_name = "mir/my_first_template.html"
def get_context_data(self, **kwargs):
context_data = super(MyFirstView, self).get_context_data(**kwargs)
context_data["my_special_key"] = my_special_data
return context_data
class MySecondView(TemplateView, BaseContextMixin):
template_name = "mir/my_second_template.html"
def get_context_data(self, **kwargs):
context_data = super(MySecondView, self).get_context_data(**kwargs)
context_data["my_special_key_2"] = my_special_data_2
return context_data
This way you avoid redundant code and at the same time you can keep the structure simple
I would suggest using separate views, unless you want to have a similar formatting structure throughout, then maybe you would just use one view. Even then, though, you can use separate views with a similar HTML structure. It really depends on how you want your documents structured.

Decorate GET URL using forms

I have some question:
I use django form, and fields like MultipleChoiceField
in view.py I clean data and get GET URL like this
http://localhost:8000/?category=&style=&sex=&brand=ASICS&brand=Be+Positive&low_price=&high_price=
Give me advise, can I regroup brand field and hide empty.
I want getting something like this:
http://localhost:8000/?brand=1+2
And else one question:
How can I set empty value(empty_label) for forms.ModelMultipleChoiceFIeld
forms.py:
brand = forms.MultipleChoiceField(required=False,
widget=forms.SelectMultiple(attrs={'size':1})
)
def __init__(self,app_label=None, *args, **kwargs):
super(Search, self).__init__(*args, **kwargs)
self.fields['brand'].choices = [('', 'All brands')]+[(brand.name, brand) for brand in Brand.objects.all() ]
views.py:
if request.method == 'GET' and request.GET:
form = SearchForm(app_label, request.GET)
if form.is_valid():
brands = form.cleaned_data['brand']
kwargs.update({"brand__name__in": brands})
This is how the browser submits multiple data. It's part of the HTML specification, trying to change it would be folly and technically I can't understand why you would try to care about how your url GET data looks.
That being said, if you want to change the way it submits you'll need javascript to transform the data on form submit. Django has nothing to do with the matter.
Using jQuery for example:
$('#form').submit(function(){
//Get form data
//Transform into my custom set of vars
//Redirect to form's ACTION with my querystring appended.
});
Please keep in mind you will not get any automatic parsing of the values on the Django side. Normally it would turn it into a list for you, but now you're responsible for parsing the 'value+value+value' yourself.
For empty label in forms you could do this -
class SomeForm(forms.Form):
h=forms.CharField(label=u'',widget=forms.TextInput(attrs={'value':'Search'}))
By keeping label as '', you get the label as empty. The attrs are basically the HTML attributes of the form text field.
UPDATE: I didn't understand the first part of your Q, elaborate...

Using FormWizard and saving the forms data in between before the completion of the whole process?

I am using FormWizard to complete a set of operation in my app, I have two models Employee and Person, Employee class inherits Person, and all the fields of Person are available for Employee object.
Now I am creating a set of forms using FormWizard, I just wanted to know that. If a user starts entering the data in the forms and fills upto 2 forms out of 4 and is willing to fill the rest of the forms afterwards. So is this possible that the data for the two forms which he filled can be saved in the database.
And the next time he comes can complete the operation form the 3rd form.
If anyone knows that then plz help me out, it would be a great help. Thank You!
what you can do is every step, save out the form state to some serialised object in db ForeignKeyed to the user.
then when hooking up the formwizard, wrap the formwizard view in a custom view which checks if the user has a saved form and if so deserialises and redirects to the appropriate step.
Edit: seems formwizard saves state in POST. only need to save postdata.
models.py:
class SavedForm(Model):
user = ForeignKey(User)
postdata = TextField()
views.py:
import pickle
class MyWizard(FormWizard):
def done(self, request, form_list):
SavedForm.objects.get(user=request.user).delete() # clear state!!
return render_to_response('done.html',)
formwizard = MyWizard([Form1, Form2]) <- class name, not instance name
def formwizard_proxy(request, step):
if not request.POST: #if first visit, get stored data
try:
prev_data = SavedForm.objects.get(user=request.user)
request.POST = pickle.loads(prev_data.postdata)
except:
pass
else: # otherwise save statet:
try:
data = SavedForm.objects.get(user=request.user)
except:
data = SavedForm(user=request.user)
data.postdata=pickle.dumps(request.POST)
data.save()
return formwizard(request)
edit: changed formwizard constructor

Categories

Resources