Django using a variable in multiple views/templates - python

I have a table with results from a search. This means there are no models here. The problem is that i'd like to go to a detail page for a clicked item, but I am not sure if I can do that without putting it in the URL.
Right now it is done like this:
In my .html for each item in the table:
view more</td>
In my urls.py
url(r'^track/(?P<title>.+?)/$', detail,
name='detail'),
In my detail view, where it uses the variable:
def detail(request, title):
if request.method == 'GET':
...
Now this might work, but it is not ideal for me. The url contains whitespace and is not urlencoded, because I need the variable like it is. I was wondering if there is some easier, or better way to pass this variable to a different view or template

The right way to do this is to define a get_absolute_url method on the model class for your item model. This is typically done using reverse, so it would look something like this:
def get_absolute_url(self):
from django.core.urlresolvers import reverse
return reverse('detail', kwargs={'title': self.title})
Then in your template you simply do:
view more
And the title will be properly escaped.
That said, you might want to consider using a slug instead of a title to generate your URLs.

Related

In Django how to retrieve substring till ocurrence of a dot, from a model, to show in a template

I have the following model:
class News(models.Model):
news_text = models.TextField(null=True)
... # other fields
with the following view:
def news(r):
news= News.objects
values = {'news':news}
return render(r,'webapp1/news.html',values)
I want to show in the template a substring for the column news_text, till the first 'dot' occurrence, like:
{{news.news_text| split('.')[0] }}
Tried this in template but got:
"invalid filter: 'split'".
Django has a limited set of builtin template filters and there is no split filter.
Also in Django templates you can access object's attributes or methods like {{my_dict.keys}}, but you can't pass any arguments. So you can't do things like {{news.news_text.split('.')}} as well.
All of this is done with intention to force you to separate logic from templates. So for your example probably will be better to define a special context variable and pass it into template's rendering context, like:
def news(r):
news = News.objects.all().get() # don't forget to call some filters on object manager
context = {
'news': news,
'headlines': news.news_text.split('.')[0],
}
return render(r, 'webapp1/news.html', context)
Also note that plural model names may be confusing: is it an array of news in each entry, or not?
Nevertheless you can create custom template tags and filters (and in many cases you should) to solve your problem.

Get Current Url Without Parameters in Django

I have a url like below:
url(r'^board/(?P<pk>\d+)$', board_crud, name='board_update'),
I want to get current view 'board' without parameters so that i can redirect to it.
I want to redirect to current view(without param) in same view(with param).
Thanks in Advance.
I believe you want to do something like this:
urls.py
url(r'^board/$', board_redirect, name='board_redirect'),
url(r'^board/(?P<pk>\d+)/$', board_crud, name='board_update'),
PS: Note the ending /, it's a good idea to always end the url
patterns with a forward slash, for consistency (except cases where you
are return a url like sitemap.xml for example).
Then, you would need to create a view like this:
views.py
from django.shortcuts import redirect
from .models import Foo
def board_redirect(request):
latest = Foo.objects.values('pk').order_by('-date').first()
return redirect('board_update', pk=latest['pk'])
The queryset would define the logic you want to implement. I don't have more info on your application. In this example you would always show the "latest" object based on a "date" field. Hope it makes sense.

Django - using regex in urls.py to pass values

I just ran into a problem..
I'm trying to build a website at the moment with different pages.
So I created a django app called pages, with the following fields:
title
text
URL
The idea is, that users can create new pages and delete existing ones and it actually affects the navigation in real time.
So in my urls.py I wanted to handle this somehow like this:
from django.conf.urls.defaults import *
urlpatterns = patterns('',
(r'^pages/(/w)', pages.views.display_content),
)
For example there could be a page with URL property "page1", then "page1" should be stored by (/w) and passed over to pages.views.display_content, which then would display the corresponding data. The "page1" page would be accessible through domain.com/pages/page1.
However, as I'm not really good with regex, I think I really need your help.
I would be really happy if someone could explain to me how I have to write my URL rule for this..
Good Night :)
In addition, you could name the parameter that will be captured and passed to your view function with this notation:
...
(r'^pages/(?P<page_name>\w+)', 'pages.views.display_content'),
...
So you can access it with that name in your view function.
Its header should look like this:
def display_content(request, page_name):
...

Avoid passing RequestContext in every render in django

I am developing a site right now that, once you are logged in, a search bar will always be present on the top of the page. I am wondering what the best way design this paradigm in Django. Currently, I have a separate file called forms.py that sits at settings.py level in my folder hierarchy. In almost every view, I have to add:
from forms.py import SearchForm
and then in every single render call, I have to pass:
form = SearchForm()
return render('somepage.html',{"search_form" : form},c=RequestContext())
I have looked around for a better way of doing this, but I am having trouble finding anything useful. I have a feeling that the current design I am using is not ideal, since I am required to import/pass as parameter in almost every view.
The form is defined in a base.html, so I am using template inheritance, but I still need to pass the form object to every render as far as I can tell.
Use a context processor
Add your search form to the context of all views using RequestContext, which the new render you're using does automatically.
def FormContextProcessor(request):
if request.user.is_authenticated():
return {'form': SearchForm() }
return {}
You said it's used in nearly all views, and this is hardly an expensive operation instantiating a form, so I'd use this solution.
with django < 1.3 you can have a decorator, which can take care of rendering a html:
def search_render(function):
# return a decorated function which will take template from the args
# take output of the inner function (this should be a dictionary e.g. data = ..
# instantiate SearchForm
# add SearchForm instance to the data dictionary
# and return render(template, data, RequestContext(request))
#search_render(tamplate='somepage.html')
def my_other_view(request):
return {'data':'value'}
With django >= 1.3 you can use a class based views, with similiar approach.

Keeping filters in Django Admin

What I would like to achive is:
I go to admin site, apply some filters to the list of objects
I click and object edit, edit, edit, hit 'Save'
Site takes me to the list of objects... unfiltered. I'd like to have the filter from step 1 remembered and applied.
Is there an easy way to do it?
Unfortunately there's no easy way to do this. The filtering does not seem to be saved in any session variable.
Clicking back twice is the normal method, but it can be unweildy and annoying if you've just changed an object so that it should no longer be shown using your filter.
If it's just a one-off, click back twice or go through the filtering again, it's the easiest way.
If you're going to be filtering more often, or you just want to learn about hacking the admin (which is pretty open and easy), you'll want to write a FilterSpec.
Have a look here and here for examples of people writing their own.
A really, really terrible way to do this would be to edit the admin interface so that after you click "Save", you are redirected to you filtered URL. I wouldn't recommend this at all, but it's an option.
Another fairly simple way to do this would be to write a generic view to show your filtered objects, then use Django forms to edit the items from there. I'd have a look at this, you'll be stunned just how little code you have to write to get a simple view/edit page going.
Click 2 times "Back"?
There's a simple hack to do this, but it's not a general solution and requires modifying every ModelAdmin which you want to support this. Maybe there is a general way to do this, but I've not spent the time to solve it on a general level.
The first step is to write a custom FilterSpec for the filter (see Harley's post for links that will help) which saves the chosen filter value in the session (and deletes it when no longer wanted).
# in cust_admin/filterspecs.py
from django.contrib.admin.filterspecs import FilterSpec, ChoicesFilterSpec
class MyFilterSpec(ChoicesFilterSpec):
def __init__(self, f, request, params, model, model_admin):
super(MyFilterSpec, self).__init__(f, request, params, model,
model_admin)
if self.lookup_val is not None:
request.session[self.lookup_kwarg] = self.lookup_val
elif self.lookup_kwarg in request.session:
del(request.session[self.lookup_kwarg])
# Register the filter with a test function which will apply it to any field
# with a my_filter attribute equal to True
FilterSpec.filter_specs.insert(0, (lambda f: getattr(f, 'my_filter', False),
MyFilterSpec))
You must import the module this is in somewhere, for example your urls.py:
# in urls.py
from cust_admin import filterspecs
Set a property on the field you want to apply the filter to:
# in models.py
class MyModel(models.Model):
my_field = Models.IntegerField(choices=MY_CHOICES)
my_field.my_filter = True
In a custom ModelAdmin class, override the change_view method, so that after the user clicks save, they are returned to the list view with their filter field value added to the URL.
class MyModelAdmin(admin.ModelAdmin):
def change_view(self, request, object_id, extra_context=None):
result = super(MyModelAdmin, self).change_view(request, object_id,
extra_context)
if '_save' in request.POST:
if 'my_field__exact' in request.session:
result['Location'] = '/admin/myapp/mymodel/?my_field__exact=%s' \
% request.session['my_field__exact']
return result
Another way to do this is to embed the filter in the queryset.
You can dynamically create a proxy model with a manager that filters the way you want, then call admin.site.register() to create a new model admin. All the links would then be relative to this view.
In my opinion its better to override methods from ModelAdmin changelist_view and change_view:
Like so:
class FakturaAdmin(admin.ModelAdmin):
[...]
def changelist_view(self, request, extra_context=None):
result = super(FakturaAdmin, self).changelist_view(request, extra_context=None)
request.session['qdict'] = request.GET
return result
def change_view(self, request, object_id, extra_context=None):
result = super(FakturaAdmin, self).change_view(request, object_id, extra_context)
try:
result['location'] = result['location']+"?"+request.session['qdict'].urlencode()
except:
pass
return result
As you wish, after save object you go back to list of objects with active filters.
There is a change request at the Django project asking for exactly this functionality.
All it's waiting for to be checked in is some tests and documentation. You could write those, and help the whole project, or you could just take the proposed patch (near the bottom of the page) and try it out.
https://code.djangoproject.com/ticket/6903
This feature has been added to Django as part of the 1.6 release and is enabled now by default. It is described in the release notes:
ModelAdmin now preserves filters on the list view after creating,
editing or deleting an object. It’s possible to restore the previous
behavior of clearing filters by setting the preserve_filters attribute
to False.

Categories

Resources