Django URLs: Can't get the keyword arg into my view? - django

The URL pattern having issues is:
url(r'^$', business_list, name='business_list_home'),
url(r'^(?P<param>\w+)$', business_list, name='business_list_results'),
My view is:
#render_to('app/business_list.html')
def business_list(request, param=None):
queryset = Business.objects.all()
search_form = SearchForm
print request.GET
if param in request.GET:
param = request.GET.get('param')
if queryset.filter(city__iexact=param).exists():
queryset = queryset.filter(city__iexact=param)
elif queryset.filter(category__iexact=param).exists():
queryset = queryset.filter(category__iexact=param)
print queryset
else:
queryset = None
print queryset
return {'business_list': queryset, 'search_form': search_form}
Essentially, I don't understand why when I go to /Miami I don't have access to it via request.GET['param'] in the view? print request.GET prints <QueryDict: {}>
The reason I want to do this is to have a nice URL scheme for displaying results of businesses for the city or category (and that's why I check if it's a city or category in the view too) in the url. Let me know if there's a better way to accomplish this like
url(r'^$', business_list),
url(r'^(?P<city>\w+)$', business_list),
url(r'^(?P<category>\w+)$', business_list),
url(r'^(?P<category>\w+)/(?P<city>\w+)$', business_list),
Thanks for any help!

business_list is a function that takes a request and a parameter called "param" (should probably give it a better name as to make it a little less generic). In Django, the URL routes can define what values get passed to the parameters of the view functions.
In the situation above, when a user goes to /Miami, Django will try to match the string "Miami" with any of the regular expressions defined in the URL routes. In this case, the matching expression is ^(?P<param>\w+)$. When this match is made, the string Miami is captured into param.
Django will then call business_list(request, param="Miami"). Note that there were no query parameters passed in the URL (e.g., /Miami?color=blue).
The issue in the code you've written above is that you're checking to see not if param exists, but rather that param was passed in the query parameters. Try going to the URL /Miami?param=test and it'll probably work the way that you expected.
The real fix here is to not reference request.GET, because using GET parameters is exactly what you're trying to avoid. So, instead of
if param in request.GET:
param = request.GET.get('param')
if queryset.filter(city__iexact=param).exists():
queryset = queryset.filter(city__iexact=param)
elif queryset.filter(category__iexact=param).exists():
queryset = queryset.filter(category__iexact=param)
print queryset
Just do:
if param:
if queryset.filter(city__iexact=param).exists():
queryset = queryset.filter(city__iexact=param)
elif queryset.filter(category__iexact=param).exists():
queryset = queryset.filter(category__iexact=param)

Related

How in django set in url unrequirement argument

All sense in title of query, i try this:
path(r'get_turnover/<str:attributes>/\?product=(?P<product>\w+$)', views.OutputTurnover.as_view(), name='get_turnover')
but it's not work for me.
The query string [wiki] is not part of the path. This is decoded by django and passed through request.GET as a QueryDict [Django-doc] a dictionary-like object.
The path should thus be:
path(
'get_turnover/<str:attributes>/',
views.OutputTurnover.as_view(),
name='get_turnover'
)
and in the view, you can access this with:
class OutputTurnover(View):
def get(request, attributes):
product = self.request.GET.get('product')
# …

Django CBV - dealing with optional parameters in URLs

I have a Class Based View to list animals from a specific herd. There are multiple herds, so the user can either see all animals from ONE herd, or all animals from ALL herds.
How do I have an optional URL parameter and handle it in the CBV?
urls:
url(r'list/(?P<hpk>[0-9]+)/$', AnimalList.as_view(), name = 'animal_list'),
url(r'list/$', AnimalList.as_view(), name = 'animal_list'),
My view:
class AnimalList(ListView):
model = Animal
def get_queryset(self):
if self.kwargs is None:
return Animal.objects.all()
return Animal.objects.filter(herd = self.kwargs['hpk']) # <--- line 19 that returns an error
Going to a URL of like /animals/list/3/ works fine, while /animals/list/ fails with an error. Here's that error:
KeyError at /animals/list/
'hpk'
Request Method: GET
Request URL: http://localhost:8000/animals/list/
Django Version: 1.8.2
Exception Type: KeyError
Exception Value:
'hpk'
Exception Location: /var/www/registry/animals/views.py in get_queryset, line 19
I get that the self.kwargs is a dictionary, and when I print() it inside the view, it'll show it's empty. But I can't figure out how to capture that scenario. I feel like this is a simple, stupid error I'm missing.
To anyone who may stumble on this and need an answer, here is my working code after figuring it out:
class AnimalList(ListView):
model = Animal
def get_queryset(self):
if 'hpk' in self.kwargs:
return Animal.objects.filter(herd = self.kwargs['hpk'])
return Animal.objects.all()
Essentially we test to see if the URL parameter hpk is present in the list of self.kwargs. If it is, we filter the queryset. Otherwise, we return all animals.
Hope this helps someone :)
I would implement this using GET parameters instead of separate URLs. With this approach, there is only one URL /list/ that is filtered by parameters, for example /list/?hpk=1.
This is more flexible as you can eventually add more queries /list/?hpk=1&origin=europe
#url(r'list/$', AnimalList.as_view(), name = 'animal_list'),
class AnimalList(ListView):
model = Animal
def get_queryset(self):
queryset = Animal.objects.all()
hpk = self.request.GET.get("hpk"):
if hpk:
try:
queryset = queryset.filter(herd=hpk)
except:
# Display error message
return queryset

How to get /test/?grid-view or /test/?list-view' in Django view

I have a page that can be viewed by list or grid view through jQuery, but I want the URL to display what view it is -- especially when user paginate so that they don't have to reclick what they want to view -- so something like this: /test/?grid_view or /test/?list_view.
I've been trying to use request.GET.get, but it doesn't seem to be working. Here is what I have so far:
def test (request):
grid_view = request.GET.get('grid_view')
list_view = request.GET.get('list_view')
if grid_view:
return render_to_response('grid_view.html', {}, context_instance=RequestContext(request))
elif list_view:
return render_to_response('list_view.html', {}, context_instance=RequestContext(request))
else:
return render_to_response('default_view.html', {}, context_instance=RequestContext(request))
Then in my main template I'd point the different views to list view, etc. There is probably a better way to do it so any suggestions are appreciated too.
An empty string is evaluated to False and, that is what your GET request Query String parameters will contain.
You could modify your code to:
if 'grid_view' in request.GET or 'grid_view/' in request.GET:
pass
elif 'list_view' in request.GET or 'list_view/' in request.GET:
pass
Or:
if request.GET.has_key('grid_view') or request.GET.has_key('grid_view/'):
# ...
That's not a GET parameter. Use request.META['QUERY_STRING'] to get the query string.

django - how can I clean variable data passed by an url?

When I'm using a form I clean field data using Django forms but how do you clean variable data that's passed by an URL?
For example I have an URL like this: http://mywebsite.com/tags/my-tag/ where my-tag is the variable that I'm passing to a function on my views.py.
I tried to use a Django form to clean the data but I'm getting en error saying "'TagForm' object has no attribute 'cleaned_data'".
I know my-form variable is reaching the tags function in the views.py since I'm able to show its content on a template so the problem is probably with the way I'm using the form.
views.py
def tags(request, my-tag):
tagform = TagForm(request.GET)
cleaned_dt = tagform.cleaned_data
form_tag = cleaned_dt['tag']
forms.py
class TagForm(forms.Form):
tag = forms.CharField()
Any ideas?
The cleaned_data dictionary attribute appears after you call is_valid method on your form.
def tags(request, my-tag):
tagform = TagForm(request.GET)
if tagform.is_valid():
cleaned_dt = tagform.cleaned_data
form_tag = cleaned_dt['tag']
return render(request, "may_template.html", {"form":tagform})
You are creating a TagForm with a request object, but you're not giving the TagForm the value of my-tag anywhere that I can see.
The /my-tag/ section of the URL isn't a request parameter. It's part of the url, and presumably passed to the view function as my-tag (you might want to rename it my_tag to be more Pythonic).
Edit
You can simple create a dict object to initialize to Form object instead of request.GET. An example is here.
data = {'tag': my_tag,
'anotherIfNecessary': 'Hi there'}
tagform = TagForm(data)
Basically, the dictionary used to populate a form object must contain a mapping of form field names to the value you wish to set it at.
In this case, you have a form field name of "tag" and want to set it to my-tag (are you sure you don't get a syntax error with the dash in the variable name? I do...). I've corrected my example.

passing object data through URL

I know that I can pass object values through a URL pattern and use them in view functions. For instance:
(r'^edit/(?P<id>\w+)/', edit_entry),
can be utilized like:
def edit_entry(request, id):
if request.method == 'POST':
a=Entry.objects.get(pk=id)
form = EntryForm(request.POST, instance=a)
if form.is_valid():
form.save()
return HttpResponseRedirect('/contact/display/%s/' % id)
else:
a=Entry.objects.get(pk=id)
form = EntryForm(instance=a)
return render_to_response('edit_contact.html', {'form': form})
But how do I pass a value from a model field (other than "id") in the url? For instance, I have an abstract base model with a field "job_number" that is shared by child models "OrderForm" and "SpecReport". I want to click on the "job_number" on the order form and call the Spec Report for that same job number. I can create an
href="/../specifications/{{ record.job_number }}
to pass the info to the url, but I already know that this regex syntax is incorrect:
(r'^specifications/(?P<**job_number**>\w+)/', display_specs),
nor can I capture the job_number in the view the same way I could an id:
def display_specs(request, job_number):
records = SpecReport.objects.filter(pk=job_number)
tpl = 'display.html'
return render_to_response(tpl, {'records': records })
Is there an easy approach to this or is it more complicated than I think it is?
the amended code is as follows:
(r'^specdisplay/?agencyID=12/', display_specs),
and:
def display_specs(request, agencyID):
agencyID= request.GET.get('agencyID')
records = ProductionSpecs.objects.filter(pk=id)
tpl = 'display_specs.html'
return render_to_response(tpl, {'records': records })
not sure how to filter. pk is no longer applicable.
Yes, you are making this a little more complicated that it is.
In your urls.py you have:
(r'^edit/(?P<id>\w+)/', edit_entry),
Now you just need to add the almost identical expression for display_specs:
(r'^specifications/(?P<job_number>\w+)/', display_specs),
Parenthesis in the regex identifies a group and the (?P<name>...) defines a named group which will be named name. This name is the parameter to your view.
Thus, your view will now look like:
def display_specs(request, job_number):
...
Finally, even though this will work, when you redirect to the view, instead of using:
HttpResponseRedirect('/path/to/view/%s/' % job_number)
Use the more DRY:
HttpResponseRedirect(
reverse('display_specs', kwargs={'job_number': a.job_number}))
Now if you decide to change your resource paths your redirect won't break.
For this to work you need to start using named urls in your urlconf like this:
url(r'^specifications/(?P<job_number>\w+)/', display_specs, name='display_specs'),
Not knowing what your model structure is like ... why couldn't you just pass the particular job's id and then pick it up with a query?
Afaik every model automatically has an id field that autoincrements and is a unique identifier of a row (an index if you will), so just change the href creation to {{record.id}} and go from there.
Try passing the job_number through the url then, especially if you don't care about pretty url's too much just do this:
url: /foo/bar/?job_number=12
no special markup to catch this btw, the regex is r'^foo/bar/'
And then read it in the view like this:
job_number= request.GET.get('job_number')
I really don't understand your question. What's the difference between passing id and passing job_number in a URL? If you can do one, why can't you do the other? And once the job_number is in the view, why can't you do a normal filter:
records = SpecReport.objects.filter(job_number=job_number)