Django: How to access other model's primary key in urlpatterns? - python

I am getting NoReverseMatch when routing to a specific path.
It is not able to find the primary key <int:pk> for only one urlpattern while getting results for all other similar paths.
I am assuming this error is because the model here is different, ex,
Not getting error for:
class PostUpdateView():
model = A
and the error I am getting is for:
class AddCommentView():
model = B
urlpatterns = [
path('post/<int:pk>/update/', PostUpdateView.as_view(), name = 'post-update'),
path('post/<int:pk>/comment', AddCommentView.as_view(), name = 'post-comment')]
Both classes are in same views.py file because I need model A's primary key in my routing url so that I can reverse to the original page.
Error:
Reverse for 'post-comment' with no arguments not found. 1 pattern(s) tried: ['post/(?P<pk>[0-9]+)/comment$']
What is the correct way to include both models' keys in same route?
Note: A's primary key is present in B as foreign key.

You get_absolute_url method should look like this the kwargs argument.
from django.urls import reverse
class AddCommentView():
model = B
def get_absolute_url(self):
return reverse('post-comment', kwargs={'pk': self.pk})

Related

QuerySet working in shell but not in Views.py - Django 1.10

When executing the following QuerySet in the Django shell (python manage.py shell):
Employee.objects.filter(restaurant__pk = 1)
I get a result back:
<QuerySet [<Employee: Joyce McDonnals>]>
Please excuse me, because I am new to Django. I am trying to implement this queryset dynamically in my webpage through the PK. The get_queryset which I have defined in the views.py is:
class EmployeeList(ListView):
template_name= "Restaurants/employee_list.html"
model = Employee
def get_queryset(self, **kwargs):
queryset = Employee.objects.filter(pk= restaurant.pk)
return queryset
But this returns the error:
NameError at /restaurant/1/employees/
name 'restaurant' is not defined
Request Method: GET
Request URL: http://127.0.0.1:8001/restaurant/1/employees/
Django Version: 1.10.5
Exception Type: NameError
Exception Value:
name 'restaurant' is not defined
I have tried some variants for the queryset in my views.py, but I can't seem to get it to work. Could someone help me to define this queryset?
The problem is not the queryset, but standard Python scoping. You need to think about where the value for restaurant is supposed to be coming from.
In your case, it is clearly coming from the URL; assuming you have a URL something like this:
url(r'^restaurant/(?P<restaurant_id>\d+)/employees/$', ...)
named capturing group, that value will be stored in self.kwargs['restaurant_id']. So you should use that in the filter.
Note also that you can use restaurant_id directly as the field, rather than doing a JOIN
So:
queryset = Employee.objects.filter(restaurant_id=self.kwargs['restaurant'])
You are not providing restaurant into the get_queryset. Probably it should be like that:
def get_queryset(self):
self.restaurant = get_object_or_404(Restaurant, name=self.args[0])
return Emploee.objects.filter(pk=self.restaurant)
try this -
queryset = Employee.objects.filter(restaurant__pk=pk_value)
you should pass pk_value here and restaurant field should be foreign key to other table. You can check by passing pk_value = 1 as you are doing in shell.
hope this helps. for more info check django model docs - https://docs.djangoproject.com/en/1.10/topics/db/models/

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.

django-rest-framework HyperlinkedIdentityField with multiple lookup args

I have the following URL in my urlpatterns:
url(r'^user/(?P<user_pk>[0-9]+)/device/(?P<uid>[0-9a-fA-F\-]+)$', views.UserDeviceDetailView.as_view(), name='user-device-detail'),
notice it has two fields: user_pk, and uid. The URL would look something like: https://example.com/user/410/device/c7bda191-f485-4531-a2a7-37e18c2a252c.
In the detail view for this model, I'm trying to populate a url field that will contain the link back to the model.
In the serializer, I have:
url = serializers.HyperlinkedIdentityField(view_name="user-device-detail", lookup_field='uid', read_only=True)
however, it's failing I think because the URL has two fields:
django.core.exceptions.ImproperlyConfigured: Could not resolve URL for hyperlinked relationship using view name "user-device-detail". You may have failed to include the related model in your API, or incorrectly configured the lookup_field attribute on this field.
How do you use a HyperlinkedIdentityField (or any of the Hyperlink*Field) when the URL has two or more URL template items? (lookup fields)?
I'm not sure if you've solved this problem yet, but this may be useful for anyone else who has this issue. There isn't much you can do apart from overriding HyperlinkedIdentityField and creating a custom serializer field yourself. An example of this issue is in the github link below (along with some source code to get around it):
https://github.com/tomchristie/django-rest-framework/issues/1024
The code that is specified there is this:
from rest_framework.relations import HyperlinkedIdentityField
from rest_framework.reverse import reverse
class ParameterisedHyperlinkedIdentityField(HyperlinkedIdentityField):
"""
Represents the instance, or a property on the instance, using hyperlinking.
lookup_fields is a tuple of tuples of the form:
('model_field', 'url_parameter')
"""
lookup_fields = (('pk', 'pk'),)
def __init__(self, *args, **kwargs):
self.lookup_fields = kwargs.pop('lookup_fields', self.lookup_fields)
super(ParameterisedHyperlinkedIdentityField, self).__init__(*args, **kwargs)
def get_url(self, obj, view_name, request, format):
"""
Given an object, return the URL that hyperlinks to the object.
May raise a `NoReverseMatch` if the `view_name` and `lookup_field`
attributes are not configured to correctly match the URL conf.
"""
kwargs = {}
for model_field, url_param in self.lookup_fields:
attr = obj
for field in model_field.split('.'):
attr = getattr(attr,field)
kwargs[url_param] = attr
return reverse(view_name, kwargs=kwargs, request=request, format=format)
This should work, in your case you would call it like this:
url = ParameterisedHyperlinkedIdentityField(view_name="user-device-detail", lookup_fields=(('<model_field_1>', 'user_pk'), ('<model_field_2>', 'uid')), read_only=True)
Where <model_field_1> and <model_field_2> are the model fields, probably 'id' and 'uid' in your case.
Note the above issue was reported 2 years ago, I have no idea if they've included something like that in newer versions of DRF (I haven't found any) but the above code works for me.

Django accessing foreign key value from api

According to the DRF documentation here, in the class-based views, we can send a value argument using a keyword pk. I am trying to pass 2 values to the GET function in the following code.
example:
class studentList(APIView):
def get(self, request, pk, pk2, format=None):
student_detail = Student.objects.filter(last_name = pk, campus_id.name = pk2)
serialized_student_detail = studentSerializer(student_detail, many=True)
return Response(serialized_student_detail.data)
In the above, the campus_id is a foreign key relation to another model and hence it is returning me an error. How can we access the vlues of the foreign key here? In my serializer, i mentioned the depth to be 1 so that it fetches the values from the foreign key relationship. How do i do it?
Use the double underscore to access a ForeignKey relationships attributes
student_detail = Student.objects.filter(last_name=pk, campus__name=pk2)

Categories