Capturing objects in Django Url - python

Suppose I have this model:
Class animals(models.Model):
name = models.CharField(max_length = 20)
and I make 3 objects of it, say ob1, ob2, ob3 with ob1.name = cat, ob2.name = dog, ob3.name = cow
Now if I have a url like this www.domain.com/cator www.domain.com/dog, How to capture /cat or /dogfrom the url and check against the names of objects of class animal?
I am trying to implement a view function that takes a parameter from the url, eg: object.name, and execute according to that object.
Any help is appreciated.

Use named groups.
It’s possible to use named regular-expression groups to capture URL
bits and pass them as keyword arguments to a view.
urls.py:
from django.conf.urls import patterns, url
urlpatterns = patterns('',
url(r'^(?P<name>\w+)/$', 'my_view'),)
views.py:
def my_view(request, name=None):
# get a model instance
animal = animals.objects.get(name=name)
Hope that helps.

Related

How to write single class based view for multiple url?

I have urls reporting?id=1 and reporting?students=bachelor. For both these urls I am using same base url 'reporting' in urls.py.
urls.py
from .views import Reporting
urlpatterns = [
path('', AnalyticsData.as_view()),
path('reporting', Reporting.as_view()),
path('index', index.as_view())
]
How could I use the same class view to execute different get methods so that for the first URL, a method to get students by id is executed and for the second URL, a method to get bachelor students is executed. For now, I am writing code in the following way
views.py
class Reporting(APIView):
def get(self, request):
id = self.request.GET.get("id")
student_type = self.request.GET.get("students")
if id:
*logic*
if student_type=="bachelor":
*logic*
But I don't know if this is the right way. Also, if there are multiple parameters than there will be many if conditions. Is there any other way available?
I would have a variable in my class based view that tells what to do when it is called. Something like this. This var variable gets the value of whatever that is passed when calling it from the urls.py so you can use that to your advantage.
class Reporting(APIView):
var = "" # HERE
def get(self, request):
id = self.request.GET.get("id")
student_type = self.request.GET.get("students")
if id:
*logic*
if student_type=="bachelor":
*logic*
Then in my urls.py I would call the class with that parameter and handle the logic accordingly.
from .views import Reporting
urlpatterns = [
path('reporting', Reporting.as_view(var="yourvalue")), #HERE
]
Then handle logic according to the var variable.
So now if you want your class to behave differently for another case, then you can change var variable accordingly and the behaviour would also change. You just need to change the value in your url.
urlpatterns = [
path('reporting', Reporting.as_view(var="yourvalue")), #HERE
path('new_reporting', Reporting.as_view(var="your_new_value"))
]
So for your case you can pass this var along to your views and then you can use if else to process the get function. Don't forget the self btw.
if self.var == "id":
# Logic for id
elif self.var == "students":
# Logic for students

Django 2.0 dynamically generate urlpatterns

I wrote this code that dynamically generates url patterns from the database. These urls have only one level path: domain.com/something.
someapp/models.py
class SomeModel(models.Model):
pattern = models.CharField(max_length=50)
name = models.CharField(max_length=50)
text = models.CharField(max_length=50)
someapp/apps.py
class SomeAppConfig(AppConfig):
name = 'someapp'
def ready(self):
from .models import SomeModel
from .urls import urlpatterns
from . import views
urls_in_db = SomeModel.objects.all()
for url_in_db in urls_in_db:
urlpatterns.append(path(url_in_db.pattern,
views.SpecialView.as_view(),
name=url_in_db.name)
someapp/views.py
class SpecialView(generic.TemplateView):
template_name = 'template/view_template.html'
model = SomeModel
def get_context_data(self, **kwargs):
context = super(SpecialView, self).get_context_data(**kwargs)
context['content'] = SomeModel.objects.get(pattern=self.request.path)
return context
Is this solution an anti-pattern? And, if so, why?
Thanks
Yes, your solution is an anti-pattern. Django supports parameters in URL patterns that get captured and become available in the corresponding view. Using these URL parameters, you can write and maintain a single URL pattern for every record of a certain type in your database.
Take a look at this example of URL parameters.
Finally, also note how your solution could potentially have very poor performance since you are potentially creating millions of URL patterns depending on the size of your database.

How to add GET parameters to django-tables2 LinkColumn

I am trying to set GET parameters on a LinkColumn based on Accessors with django-tables2.
Let's say:
urls.py
urlpatterns = [
...
url(r'^rqGET$', views.rqGET, name='rqGET'),
...
]
views.py
def rqGET(request):
#... do something with request.GET
tables.py
class MyTable(tables.Table):
id = LinkColumn('rqGet',text='Link') # do something with Accessors to make a GET string, maybe ?id=A('pk')
class Meta:
model = MyModel #(has field 'id')
I want to use reverse to get the correct url, then construct the GET parameter string. For example /rqGET?id=1 ('1' would be different in each row).
That's not really how Accessors work in django-tables2. Django-tables2 uses django's reverse to generate urls. If you want reverse to be able to generate the url, you need to define the parameters in your urls, which will be passed as arguments to your view function:
# urls.py
urlpatterns = [
...
url(r'^rqGET/(?P<id>\d+)$', views.rqGET, name='rqGET'),
...
]
# views.py
def rqGET(request, id):
# do something with request/id.
If you don't want to change the way your url is formatted, you can use a custom render_ function on your MyTable like this:
# tables.py
from django.core.urlresolvers import reverse
class MyTable(tables.Table):
id = LinkColumn('rqGet', text='Link') # do something with Accessors to make a GET string, maybe ?id=A('pk')
class Meta:
model = MyModel
def render_id(self, record):
url = reverse('rqGET')
return format_html('{}', url, record.id, 'Link')
This will render a link with format /rqGET/?id=<id>.

Django url pattern for DetailView

I was following this tutorial
Here is the model:
class Post(models.Model):
title = models.CharField(max_length=200)
pub_date = models.DateTimeField()
text = models.TextField()
slug = models.SlugField(max_length=40, unique=True)
def get_absolute_url(self):
return "/{}/{}/{}/".format(self.pub_date.year, self.pub_date.month, self.slug)
def __unicode__(self):
return self.title
class Meta:
ordering = ['-pub_date']
Here is the url for accessing a post:
from django.conf.urls import patterns, url
from django.views.generic import ListView, DetailView
from blogengine.models import Post
urlpatterns = patterns('',
url(r'^(?P<pub_date__year>\d{4})/(?P<pub_date__month>\d{1,2})/(?P<slug>[a-zA-Z0-9-]+)/?$', DetailView.as_view(
model=Post,
)),
)
What's interesting about this code is the use of double underscore in the url pattern. I did lookup django's documentation on url pattern: https://docs.djangoproject.com/en/1.8/topics/http/urls/ But I cannot find any docs on using double underscore inside url pattern.
My guess about this usage is that a keyword argument called pub_year will be passed to the view function of DetailView, and the pub_year argument has two attributes year and month.
I tried to use the following url and it still worked:
url(r'^(?P<year>\d{4})/(?P<month>\d{1,2})/(?P<slug>[a-zA-Z0-9-]+)/?$', DetailView.as_view(
model=Post,
)),
So I guess the use of double underscore is not necessary.
I found this line in Django's source code
It looks like a detailview (which inherits from SingleObjectMixin) can use slug to match a record. If that is the case, then the year and month arguments are not needed.
So here are my questions:
Is there any value in using double underscore in url pattern?
When I reduce the url pattern to the following, I only get a 404 when requesting the page with: 127.0.0.1:8000/test/ (test is the slug for an existing record stored in db)
url(r'^/(?P<slug>[a-zA-Z0-9-]+)/?$', DetailView.as_view(
model=Post,
)),
why is that?
Since this has not really been answered, yet:
Is there any value in using double underscore in url pattern?
DRY, more or less:
class SomeDetailView(...):
def get_queryset(self):
queryset = super(SomeDetailView, self).get_queryset()
# grab all named URL keyword arguments and
# transform them into field lookup patterns.
# Leave out slug and primary key keywords, though.
filter_args = {
k: v for k, v in self.kwargs.iteritems()
if k not in (self.slug_url_kwarg, self.pk_url_kwarg)
}
if filter_args:
queryset = queryset.filter(**filter_args)
return queryset
Since e.g. pub_date__year is a valid field lookup you --possible security problems nonwithstanding-- just gained the ability to add lookup criteria solely via named capture patterns in urls.py.
When I reduce the url pattern to the following, I only get a 404 when requesting the page with: 127.0.0.1:8000/test/ (test is the slug for an existing record stored in db)
url(r'^/(?P<slug>[a-zA-Z0-9-]+)/?$', DetailView.as_view(model=Post, )),
^ leading slash
That's common enough mistake that it made it into documentation:
There’s no need to add a leading slash, because every URL has that. For example, it’s ^articles, not ^/articles.
Try again with r'^(?P<slug>[a-zA-Z0-9-]+)/?$'
Documentation is, however, a little misleading, here. "There's no need" should be read as "The initial leading slash is matched automatically and is not part of the url pattern".
Those ?Ps are just capturing patterns in a regex; they are names and can be whatever you want, but will be passed as keyword arguments. They need a <name> (ex (?P<name>) ) or they will be passed as regular arguments, which is why your reduced example failed.
Personally I reserve __s for db lookups, and would use something like (?<pub_year>) as a more descriptive variable name.
There are default conventions in class-based-views that let you use standard names to write really terse code. In DetailView, 'slug' is the default slug_field and slug_url_kwarg; if you trace the dispatch() pattern through, you will see where those defaults can be tweaked/how they are used. BTW: CCBV is really useful and you should use it, and your tutorial link appears broken.

What is the Django-way of namespacing ModelAdmin.get_urls when using model(admin) inheritance?

TL;DR
Is there a way to have (namespaced,) well-named views defined when using ModelAdmin.get_urls and ModelAdmins extended by inheritance?
Preferably without resorting to ModelAdmin.model._meta or some other solution of slightly questionable nature.
Pretext
View names added through get_urls get overridden when using and inheriting from custom ModelAdmins.
That is, the view name admin:tighten gets overriden in the following example:
class Screw(models.Model):
"A screw"
class HexCapScrew(Screw):
"A hex cap screw"
class ScrewAdmin(admin.ModelAdmin):
def get_urls(self):
urls = super(ScrewAdmin, self).get_urls()
extra_urls = patterns('',
url(r'^tighten/$', self.tighten, name='tighten'),
)
return extra_urls + urls
def tighten(self, request):
pass
class HexCapScrewAdmin(ScrewAdmin):
pass
admin.site.register(Screw, ScrewAdmin)
admin.site.register(HexCapScrew, HexCapScrewAdmin)
On shell the following happens:
In [1]: reverse('admin:tighten')
Out[1]: u'/admin/parts/hexscrew/tighten/'
This is of course understandable since the registration of HexCapScrewAdmin overides the tighten in ScrewAdmin however now it's impossible to reverse ScrewAdmin.tighten.
A preferred solution
However I would like to be able to
reference both views separatedly and
preferably have views in their own instance namespaces.
Progress so far
The best I've come up with is the following setup (can be copy&pasted directly to some app for testing):
from django.contrib import admin
from django.db import models
class Screw(models.Model):
"A screw"
class Meta:
app_label = 'parts'
class HexCapScrew(Screw):
"A hex cap screw"
class Meta:
app_label = 'parts'
proxy = True
class ScrewAdmin(admin.ModelAdmin):
def tighten(self, request):
pass
def get_urls(self):
urls = super(ScrewAdmin, self).get_urls()
extra_urls = patterns('',
url(r'^tighten/$', self.tighten, name='tighten'),
)
# Find out the slugified name of the model this admin is bound to
# TODO: Feels dirty
model_name = self.model._meta.model_name
# Add the to `extra_urls` to their own namespace
namespaced_extra_urls = patterns('',
url(r'^', include(extra_urls, namespace=model_name, app_name='screw')),
)
return namespaced_extra_urls + urls
class HexCapScrewAdmin(ScrewAdmin):
pass
admin.site.register(Screw, ScrewAdmin)
admin.site.register(HexCapScrew, HexCapScrewAdmin)
Now I have the following:
In [1]: reverse('admin:screw:tighten')
Out[1]: u'/admin/parts/screw/tighten/'
In [2]: reverse('admin:hexscrew:tighten')
Out[2]: u'/admin/parts/hexscrew/tighten/'
In [3]: reverse('admin:screw:tighten', current_app='hexscrew')
Out[3]: u'/admin/parts/hexscrew/tighten/'
which is nice and works but includes a bit of hackery.
Is this the best that's available or am I just missing something? Any suggestions?
(At least one other way would be to do as Django's ModelAdmin.get_urls use ModelAdmin.model._meta to parametrize the view names but then I would use the namespaces.)
If you look at the way the admin does it here, you will see that in addition to defining the url, the model admin also prefixes the app_label and model_name to the url name, thus avoiding the subclassing issue to begin with. It also has the advantage of securing the view against unauthorised users (using the self.admin_site.admin_view decorator). Your get_urls() method would then become:
def get_urls(self):
from django.conf.urls import url
def wrap(view):
def wrapper(*args, **kwargs):
return self.admin_site.admin_view(view)(*args, **kwargs)
return update_wrapper(wrapper, view)
info = self.model._meta.app_label, self.model._meta.model_name
urlpatterns = super(ScrewAdmin, self).get_urls()
urlpatterns.append(
url(r'^tighten/$', wrap(self.tighten), name='%s_%s_tighten' % info))
return urlpatterns
Then, you'd look up your url like: reverse('admin:app_screw_tighten') or reverse('admin:app_hex_screw_tighten').

Categories