URL template optional param - django

Let's consider this template part
<form class="form-horizontal" action="{% if client_id %}
{% url client_edit client_id=client_id %}{% else %}
{% url client_edit %}
{% endif %}" method="post">{% csrf_token %}
{{ client_form }}
</form>
As you can see the parameter client_id is optional.
Is there a way to avoid this repetition (url client_edit) ?
Url pattern:
url('^client/edit$', client_edit, name='client_edit'),
url('^client/edit/(?P<client_id>\d+)$', client_edit, name='client_edit'),

It's not repetition of using
{% url client_edit %}
since you actualy define two urls. If you really want to make it shorter (not necessary simplier) you could create some filter like
{{client_id|make_url}}
and inside filter you can resolve to proper url

urls don't have optional parameters. You can have multiple patterns point to the same view (as you have done), and then check for the defaults in the view. In your template, {% url client_edit client_id=client_id|default_if_none:-1 %}, then depending on what you want to happen on the view end filter appropriately:
def client_edit(request, client_id = None):
if client_id:
client = get_object_or_404(Client, pk=client_id)
else:
# Default value for client
client = Client.objects.filter(active=True) # for example
# your normal logic here

Related

How to save a django "with" template tag value and reuse it in other templates?

I have a template tag that reads values from the URL. For example
the searched term is cancer. After searching, the next page that appears would have a with Searched terms: Cancer. And I would like the value cancer to appear in all of my webpages until the user does a new search.
Pages I have work this way:
Search.html > display.html > explore.html
Search.html is where the user enters what they want to search for.
I have a searchedterms.html which is included into all 3 templates and contains the Javascript code to read from the URL.
By using a JavaScript code, I managed to display searched terms: cancer in display.html but in explore.html the value is empty. I want to be able to save "cancer" to the tag and use it in other templates.
?conditions=cancer
in search.html:
<input required="" type="text" name="conditions">
in display.html:
{% with searched_terms='searchedterms.html' %}
{% include searched_terms %}
{% endwith %}
in explore.html:
{% with searched_terms='searchedterms.html' %}
{% include searched_terms %}
{% endwith %}
in searchedterms.html:
<div id="searched" class="container"></div>
<script type="text/javascript">
var urlParams = new URLSearchParams(window.location.search);
function getUrlParameter(name) {
name = name.replace(/[\[]/, '\\[').replace(/[\]]/, '\\]');
var regex = new RegExp('[\\?&]' + name + '=([^&#]*)');
var results = regex.exec(location.search);
return results === null ? '' : decodeURIComponent(results[1].replace(/\+/g, ' '));
};
var condition = getUrlParameter('conditions');
document.getElementById("searched").innerHTML = "Searched Terms: " + condition;
</script>
Actual results: Cancer appears in display.html but not in explore.html. When display.html is refreshed "cancer" also disappears.
Desired Results: Cancer appears in display.html and explore.html until the user starts a new search.
I think Django Inclusion tag is what you are looking for.
Register your tag in templatetags as below:
#register.inclusion_tag('searchedterms.html')
def searched_terms(query):
return {
'query': query
}
and now in your searchedterms.html file:
<div>
your searched query is: {{ query }} {# display your query here #}
</div>
For Class Based View:
in your Search.html > display.html > explore.html files:
{% load tags %}
{% block content %}
{% searched_terms view.kwargs.conditions %} {# here you pass your kwargs from url #}
<div>some of your existing code. </div>
{% endblock %}
how view.kwargs.conditions work is explained here Access kwargs from a URL in a Django template
For Functional View:
If you want url kwargs in your template from functional views then you can get url kwargs in view and pass it as context data:
def search_view(request, **kwargs):
"""
your existing codes
"""
context = {'conditions': kwargs.get('conditions')}
return render(request, 'search.html', context)
Using request in the template to access from url:
If you want to access the data from url in template then you can also use request.GET or request.POST based on how you want to access the data as in your Search.html > display.html > explore.html files:
{% load tags %}
{% block content %}
{% searched_terms request.GET.conditions %} {# here you access data from url #}
<div>some of your existing code. </div>
{% endblock %}
you can look for django documentation HttpRequest objects for what you can have access with request.

No reverse match error but the function exists?

I am trying to implement a renew function for a key inventory system. But when I render that page, it shows a Reversematcherror even though I mapped the correct URL and used the correct function name.
Here is my template:(The URL tag is on the super long line all the way to the right)
{% block content %}
<h1>All Borrowed Keys</h1>
{% if keyinstance_list %}
<ul>
{% for keyinst in keyinstance_list %}
<li class="{% if keyinst.is_overdue %}text-danger{% endif %}">
{{keyinst.roomkey}}
({{ keyinst.due_back }})
{% if user.is_staff %}
- {{ keyinst.borrower }}
{% endif %}
{% if perms.catalog.can_mark_returned %}
- Renew
{% endif %}
</li>
{% endfor %}
</ul>
{% else %}
<p>There are no keys borrowed.</p>
{% endif %}
{% endblock %}
My urls.py:
path('key/<uuid:pk>/renew/', views.renew_key_user, name='renew-key-user'),
path('key/<int:pk>/detail', views.KeyDetailView.as_view(), name='roomkey-detail'),
Views.py:
#permission_required('catalog.can_mark_returned')
def renew_key_user(request, pk):
"""
View function for renewing a specific keyInstance by admin
"""
key_inst=get_object_or_404(KeyInstance, pk = pk)
# If this is a POST request then process the Form data
if request.method == 'POST':
# Create a form instance and populate it with data from the request (binding):
form = RenewKeyForm(request.POST)
# Check if the form is valid:
if form.is_valid():
# process the data in form.cleaned_data as required (here we just write it to the model due_back field)
key_inst.due_back = form.cleaned_data['renewal_date']
key_inst.save()
# redirect to a new URL:
return HttpResponseRedirect(reverse('all-borrowed-keys') )
# If this is a GET (or any other method) create the default form.
else:
proposed_renewal_date = datetime.date.today() + datetime.timedelta(weeks=3)
form = RenewKeyForm(initial={'renewal_date': proposed_renewal_date,})
return render(request, 'catalog/roomkey_renew_user.html', {'form': form, 'keyinst':key_inst})
class KeyDetailView(generic.DetailView):
model = RoomKey
The error is saying
Reverse for 'views.renew_key_user' not found. 'views.renew_key_user'
is not a valid view function or pattern name.
Update this line in your template.
Renew
as name in url is renew-key-user
path('key/<uuid:pk>/renew/', views.renew_key_user, name='renew-key-user'),
Your URL name contains - hyphen not _ underscore
change this renew_key_user to renew-key-user in your template
Renew
Your template is asking for 'roomkey-detail' but the urls snippet you've provided only shows a url named 'renew-key-user'. Unless there are more url definitions you're not showing us, the code is failing as expected since it can't find a URL with the name you're asking for.

Using {% url 'view_name' object %} directly

The approach of doing {% url 'view_name' object.pk object.parent.slug %} isn't really flexible when switching to completety different url patterns. I'm looking for a way to do {% url 'view_name' object %} and to transcribe myself from object to object.pk and object.parent.slug in the url.
Like that
- template.html
{% url 'view_name' object %}
- urls.py [not real regex regex]
url('<object__parent__slug>/<object__pk>', views.view_name, name="view_name")
I know this is not at all possible with this syntax, but it's just to give an idea of what I'm looking for.
I will just add url methods inside my models:
class House(model.Models):
name = models.TextField()
....
def get_edit_url(self):
return reverse('view_name', {'pk':self.pk, 'name':self.name, 'owner_pk' : self.owner.pk })

Combine built-in tags in templates with variables

I want to combine the built-in tag: {% url %} with a dynamic url which I parse with {{ url_value }}
I tried doing: {% url 'urlname' url_value %}, but it didn't work
This is the url:
url(r'^(?P<slug>[^/]+)/$', 'reviews.views.single_product', name='product_detail'),
{{url_value }} just represents the slug
I think it should be like:
{% url product_detail slug=url_value %}

How to pass variable in django but not as url parameter

I have this in urls.py:
urlpatterns = patterns('',
url(r'^add_to_cart/(?P<app_label>\w+)/(?P<model_name>\w+)/(?P<obj_id>\d+)/$', AddToCart.as_view(), name='add-to-cart'),
)
and i am using this to call AddToCart view in template:
{% for eg in eyeglasses %}
<p>{{eg}} <a href="{% url 'add-to-cart' eg|app_label eg|class_name eg.pk %}" >Buy</a> </p>
{% endfor %}
This ends up in having a url like this
"127.0.0.1/cart/add_to_cart/product/Sunglass/2/"
which i want to avoid. Is there any different way to pass these variables but without passing them as url parameters?
You can try passing them as querystring parameters instead of in url, so you can build url as
http://127.0.0.1/cart/add_to_cart?app_label=product&product=Sunglass&id=2
Build this in template as
{% for eg in eyeglasses %}
<p>{{eg}} <a href="{% url 'add-to-cart' %}?app_label={{eg.app_label}}&product={{eg.class_name}}&id={{eg.pk}} %}" >Buy</a> </p>
{% endfor %}
In view you can get it as
def add_cart_view(request):
....
product_name = request.GET.get('product')
...
Rather than having a list of links, create a form where you use buttons of type submit. For each button give it a value that you can retrieve from the request. When you submit the form set the method to post rather than get.
You may want to take a look part 4 of the Django tutorial.