Django handle optional url parameter - python

I want url to have optional url parameter. Primary url is:
url(r'^(?P<letnik_id>[1-4])/(?P<classes_id>[A-G])/(?P<subject_id>[\w\-]+)$', views.subject, name="subject_id"),
but after subject id i want to be able to add optional parameter that is always a number:
url(r'^(?P<letnik_id>[1-4])/(?P<classes_id>[A-G])/(?P<subject_id>[\w\-]+)/(?P<digit>\d+)/$'', views.subject, name="subject_id_optional"),
Im not even sure if did that correctly, as i dont know how to set number for parameter. So after the parameter is passed i want the (template?) or maybe view to read the number (which is model's ID number) and add css class ) .highlited-model {background-color: red;} to that model.
How would i achieve this and how should i handle it in views or template, wherever it makes more sense?

I think you can just have two urls pointing to the same view, one with your optional parameter, and one without:
urls = [
url(r'^(?P<letnik_id>[1-4])/(?P<classes_id>[A-G])/(?P<subject_id>[\w\-]+)/(?P<digit>\d+)/$', views.subject, name="subject_id_optional"),
url(r'^(?P<letnik_id>[1-4])/(?P<classes_id>[A-G])/(?P<subject_id>[\w\-]+)$', views.subject, name="subject_id"),
]
def subject(request, optional_parameter=''):
return render(
request,
"template.html",
{
"optional_parameter": optional_parameter
}
)
Then you can get the parameter in your template as you would any other variable passed into the context.

Related

Is there a way to add context to Django reverse function?

Is it possible to pass context through the reverse function in Django in some way like reverse('foo', context={'bar':'baz'})? Or is there a better workaround?
As already described by Sir WillemVanOnsem in the above comment.
You can't provide context in reverse as it only produces a string: a path, eventually it goes to view.
reverse() can only take args and kwargs, see Reversing namespaced URLs for more detail.
Reverse generates an URL. The URL can parse or supply extra context. In urls.py,
path( 'action/<str:context>/', MyView.as_view(), name='foo' )
then
reverse('app:foo', kwargs={'context':'bar:baz+quux:help'} )
will generate the URL ending
.../appname/action/bar:baz+quux:help
and your view will parse context:
context = self.kwargs.get( context, '')
context_dir = {}
for kv in context.split('+'):
keyval = kv.split(':')
context_dir[ keyval[0].strip() ] = keyval[1].strip()
or something like this, ending with context_dir as {'bar':'baz', 'quux':'help'}
Alternatively you can append a querystring to the URL returned by reverse and retrieve that in the view you redirect to via request.GET
url = reverse('foo') + '?bar=baz&quux=help'
redirect, and then in that view request.GET.get('bar') will return "baz" etc.
Finally you can stuff an almost arbitrarily complex context into the user's session (which gets stored either as a cookie in his browser, or an object in your database). This is the most general but also the most complex. See the doc for using Django sessions

How to route the URL to separate views based on the value of a certain query parameter in urls.py Django?

I've recently started with Django and I'm unable to understand how to route the URL based on the value of a 'action' parameter which'll be passed along with some other parameters in the URL. So the goal is to filer out the value of the 'action' parameter and routing the url to seperate views accordingly.
For example..
If the URL is:-
/api/?param1=value&param2=value&action=status
it should be routed to the status view
if it's
/api/?param1=value&action=add&param2=value
be routed to the add view
and so on no matter the value and position of the other parameters.
I'm unable to understand how to route the URL based on the value of a 'action' parameter which'll be passed along with some other parameters in the URL.
The part after the question mark (?) is called the querystring [wiki]. It is not part of the path, hence the Django routing mechanism does not take it into account. You can access these parameters with request.GET [Django-doc], which is a QueryDict object [Django-doc]. A QueryDict is a dictionary-like object, but a key can map to multiple values.
You will need to perform the correct action in the view itself. So you can implement a path like:
# myapp/urls.py
from django.urls import path
from myapp import views
urlpatterns = [
path('api/', views.myapi, name='api')
]
In the view you can then specify how to act accordingly:
# myapp/views.py
def myapi(request):
if request.GET.get('action') == 'add':
# …
pass
if request.GET.get('action') == 'status':
# …
pass
# …
However in Django web applications, often the action is part of the path. So one makes urls that look for example like:
api/model1/
api/model1/pk
api/model1/add

Django Rest Framework for function based views

I'm having trouble getting the JSON for function based views in django. I have the below code. I basically would like the function to return either json or an html page based on the user request.
#api_view(['GET'])
#renderer_classes((JSONRenderer,TemplateHTMLRenderer,BrowsableAPIRenderer))
def meld_live_orders(request):
if request.method =='GET':
current_orders = Meld_Sales.objects.values_list('TicketNo',flat=True).distinct()
prev_orders = Meld_Order.objects.values_list('TicketNo',flat =True).distinct()
live_orders = live_order_generator(current_orders,prev_orders)
return render(request,'live_orders.html',{'live_orders':live_orders})
When i go to the url - http://localhost:8000/live-orders.json
I'm getting an error which states the below -meld_live_orders() got an unexpected keyword argument 'format'
Is this because i need to include the serializer class somewhere the same way in CBVs? Doesnt the #API_VIEW serialize the response?
i tried including format = '' in the function argument. but the problem is that it still renders html when i want it to render json.
You need to make some changes to your code.
Firstly, you need to use format_suffix_patterns in your urls if you have not defined it. This will allow us to use filename extensions on URLs thereby providing an endpoint for a given media type.
from rest_framework.urlpatterns import format_suffix_patterns
urlpatterns = [
...
]
urlpatterns = format_suffix_patterns(urlpatterns, allowed=['json', 'html']) # allow us to use '.json' and '.html' at the end of the url
Secondly. your view does not have a format parameter in the definition.
When using format_suffix_patterns, you must make sure to add the
'format' keyword argument to the corresponding views.
#api_view(['GET'])
#renderer_classes((JSONRenderer,TemplateHTMLRenderer,BrowsableAPIRenderer))
def meld_live_orders(request, format=None): # add a 'format' parameter
...
Thirdly, you need to return a DRF response and not a Django response which you are returning at the end of the view.
You must have match a format parameter in the url pattern, but in the view function there is not an argument named format. Change the view definition into:
def meld_live_orders(request, format = ""):

How to add parameters to urls in Django?

I have a view that filters the field called "defaultfieldname" in a certain object_list. What I want to do is to adapt it to pass the name of the field as as parameter in urls.py, so I could use different urls for different fields.
I am not sure which way would be easier:
url(r'^calendar/birthday/$', login_required(MonthCalends.as_view(model=Person)), name='bday_list', filter_field="birthdate"),
url(r'^calendar/deathday/$', login_required(MonthCalends.as_view(model=Person)), name='dday_list', filter_field="deathdate"),
or
url(r'^calendar/birthday/$', login_required(MonthCalends.as_view(model=Person, filter_field="birthdate")), name='bday_list'),
url(r'^calendar/deathday/$', login_required(MonthCalends.as_view(model=Person, filter_field="deathdate")), name='dday_list'),
Then I have a view:
class MonthCalends(ListView):
template_name='month_list.html'
## Sets default fieldname value
filter_field = "defaultfieldname"
...rest of code
The param in urls.py should overwrite the "defaultfieldname" on the view, but I don't know how to get the filter_field from the urls.py in the view. Any help?
Thanks!
The arguments you send with as_view are set on the MonthCalends object. That means filter_field is available as self.filter_field. Assuming you have defined the get method you could do as follows:
class MonthCalends(ListView):
template_name='month_list.html'
## Sets default fieldname value
filter_field = "defaultfieldname"
def get(self, request, *args, **kwargs):
try:
# if the filter field was sent as an argument
filter_field = self.filter_field
except:
# else revert to default
filter_field = MonthCalends.filter_field
# ...rest of code
For a more full explanation check the Django class based views documentation.
You may just use one url, that triggers the second part of your url:
url(r'^calendar/(\w+)$', login_required(MonthCalends.as_view(model=Person)), name='bday_list'),
Then you may access it using self.args[0]
And in case you just permit two different types for filter_field, you may just raise an exception later in the class that you have read self.args[0].
Of course, you may use more readable syntax in the regex like:
r'^calendar/(?P<type>\w+)$'
In this case you can access it using self.kwargs['type'].
Anyway, using regex groups seems much neater.

Django: How to write the reverse function for the following

The urlconf and view is as follows:
url(r'^register/$',
register,
{ 'backend': 'registration.backends.default.DefaultBackend' },
name='registration_register'),
def register(request, backend, success_url=None, form_class=None,
disallowed_url='registration_disallowed',
template_name='registration/registration_form.html',
extra_context=None):
What i want to do is redirect users to the register page and specify a success_url. I tried reverse('registration.views.register', kwargs={'success_url':'/test/' }) but that doesn't seem to work. I've been trying for hours and can't get my mind around getting it right. Thanks
If you want to be able to specify reverse() with parameters, those parameters have to be defined in the URL configuration itself (regexp). Something like:
url(r'^register/(?P<success_url>[\w\/]+)/$',
register,
{ 'backend': 'registration.backends.default.DefaultBackend' },
name='registration_register'),
You can wrap that URL section in ()? to make it optional (So that it matches just simple register/ too)
The difference between args and kwargs is that with args you can specify unnamed/named URL params while with kwargs only named.
So:
r'^register/(?P<success_url>\w+)/$'
reverse('url_name', args=[my_success_url])
reverse('url_name', kwargs={'success_url': my_success_url}) // both work and do the same
r'^register/(\w+)/$'
reverse('url_name', args=[my_success_url]) // only this works
Edit:
For success_url params, if you want to be able to match any full relative URL, including possible GET params in the relative URL, the actual regexp could get pretty complex.
Something like (untested):
r'^register/(?P<success_url>[\w\/]+(((\?)([a-zA-Z]*=\w*)){1}((&)([a-zA-Z]*=\w*))*)?)/$'
Edit: Sorry, completely misread the question - I didn't look at the function definition. Actually, the issue here is that your URLconf is designed in such a way as to make it impossible to set the success_url dynamically. It has to be passed explicitly to the function via the extra_context dictionary - ie the one where you have currently defined backend. Since there is nothing in the URL itself to accept this parameter, it has to be hard-coded there.

Categories

Resources