Django Detailview display properties of parent - python

I'm trying to use DetailView. I don't have a deep understanding of it.
What I'm trying to do is display the properties of an object from the pk. That is, I'm at, say, /notendur/34, and I want to display information about the object with pk=34.
I'm trying to make sense of this:
https://docs.djangoproject.com/en/dev/intro/tutorial04/
But I can't make sense of it. Perhaps one of you can help me understand? I'm looking at the second block of code in that link, not the first one.
{% extends "index.html" %}
{% block content %}
{{ "placeholder" }}
{% endfor %}
{% endblock %}
I'm looking to use the HTML to fetch the pk from /notendur/34 for example.

The detail view automatically pass object with primary key 34 named as object in context. You can access that in template e.g. {{ object.pk }} or {{ object.some_property_name }}

First of all in your views you need to load the appropriate class:
from django.views.generic import (
DetailView,
)
According to the Class Based View Inspector (keep a ref on this link):
http://ccbv.co.uk/
The DetailView has the following properties:
content_type = None
context_object_name = None
http_method_names = [u'get', u'post', u'put', u'patch', u'delete', u'head', u'options', u'trace'] View
model = None
pk_url_kwarg = 'pk'
queryset = None
response_class = <class 'django.template.response.TemplateResponse'>
slug_field = 'slug'
slug_url_kwarg = 'slug'
template_name = None
template_name_field = None
template_name_suffix = '_detail'
As you can see from the above, when the DetailView is called, it will check first for the existence of a pk or slug argument in the request,
this is done in your urls.py file:
urlpatterns = patterns('',
...
url(r'^view/(?P<slug>[\d]+)/$', MyTestDetailView.as_view(), name='myurl-name'),
...
)
By defining the slug parameter in the url, the DetailView knows which item you request details for (alternative you could use pk, but slug makes more friendly urls).
It then will fetch the model or the queryset (defined in your view) based on either the slug or pk field, this is performed in the def get_object(self, queryset=None) method.
After grabbing the model (if it fails it raises a 404 error) you can use the object within your template (specified under the template_name property) as:
{{ object }}
If you want to change the name of the template variable, you can assign a context_object_name property. A quick example is bellow:
from django.views.generic import (
DetailView,
)
from myapp.models import (
MyModel,
)
class MyTestDetailView(DetailView):
"""
Set context object name to mytemplatevar
"""
context_object_name = "mytemplatevar"
"""
Define the model to use
"""
model = MyModel
"""
Define the template
"""
template_name = "myapp/detail_view.html"
Appart from that you don't need anything else, in your template then you can access your object:
{{ mytemplatevar.something }}

Related

Query two models in Django into one queryset

I have two models in my django app:
class Person(models.Model):
name = models.CharField(max_length=100)
class Book(models.Model):
title = models.CharField(max_length=100)
persons = models.ManyToManyField(Person, related_name="books")
Now I need to create a view that will make one query for regex to both of the models, find the ones that are matching and display them in template.
If I do:
class SearchListView(ListView):
queryset = Person.objects.filter(name__icontains="a")
book_queryset = Book.objects.filter(title__icontains="a")
I get an error that ListView accepts only one queryset.
What is the typical solution to such problem?
You need to do something a little bit different here:
class SearchListView(ListView):
queryset = Person.objects.filter(name__icontains="a")
def get_context_data(self, **kwargs):
context = super(SearchListView, self).get_context_data(**kwargs)
context['book_queryset'] = Book.objects.filter(title__icontains="a")
return context
Then in your view you can do somenting like the following:
{% for object in object_list %}
<p>{{object}}</p>
{% endfor %}
{% for object in book_queryset %}
<p>{{object}}</p>
{% endfor %}
The reason why the way you are using is not working is because ListView inherit from MultipleObjectMixin the queryset property and that property is passed to object_list context variable in the template, that happens under the hood and if you want to pass more context variables to the template you need to follow the approach I shared.

How to query automatically in Django?

I can retrieve data by explicitly giving the 'Id' but I want to retrieve it automatically the top 3 'Id' data. How can I do that. please check my code below and help me out
this is models.py
class Post(models.Model):
title = models.CharField(max_length=200, unique=True)
def __str__(self):
return self.title
this is admin.py
class PostAdmin(admin.ModelAdmin):
list_display = ('id','title')
admin.site.register(Post, PostAdmin)
this is views.py
def index(request):
post3 = get_object_or_404(Post, id=3)
post2 = get_object_or_404(Post, id=2)
post1 = get_object_or_404(Post, id=1)
context = {
"post3": post3,
"post2": post2,
"post1": post1,
}
return render(request, 'contents/index.html', context)
as you can see in the above am giving the id number and it works fine but I want it to retrieve the top id automatically
this is my remplate
<h3>{{post3.title}}</h3>
<h3>{{post2.title}}</h3>
<h3>{{post1.title}}</h3>
You can just use slice on queryset (it will automatically add LIMIT to the SQL query) or take objects specified by index:
posts = Post.objects.all()[:3] # 3 first objects
posts = Post.objects.order_by("-id")[:3] # 3 last objects
post = Post.objects.last() # last object
post = Post.objects.order_by("-id")[0] # also last object, but it's better to use `last` in such case
post = Post.objects.order_by("-id")[1] # pre-last object
Pass it to the context:
context = {"posts": posts}
And then you can process this queryset in template:
{% for post in posts %}
<h3>{{ post.title }}</h3>
{% endfor %}
you should query in views for instance posts = Post.objects.all()[0:3] and then in template use for loop
{% for post in posts %}
{{ post.title }}
If you want to create DetailView you can pass post.id via template and add it as parameter in view, then Post = get_object_or_404(id=post_id)

How do you pass additional URL values to Django generic DeleteView?

Suppose I have models.py:
class Parent(models.Model):
name = models.CharField(max_length=20)
class child(models.Model):
name = models.CharField(max_length=20)
parent = models.ForeignKey(Parent, on_delete=models.CASCADE)
Now in the Detail View of a Parent I list out the children that belong to the parent.
The urls.py for this would look like
path('parents/<int:pk>/, views.ParentDetailView.as_view(), name='parent-detail')
And I'd use a django DetailView in my views.py like this
class ParentDetailView(DetailView):
model = Parent
Now in this detail view I list out the children of the parent with something like parent_detail.html in my templates
{{parent}}
{% for child in parent.child_set.all %}
{{ child.name }}
{% endfor %}
But I'd like to be able to delete the child from the database from here on this page, so I'd add something like
{{parent}}
{% for child in parent.child_set.all %}
{{ child.name }}
Delete child
{% endfor %}
Here's where I'm stuck!
I would love to have something like this in my urls.py
path('parents/<int:pk>/', views.ParentDetailView.as_view(), name='parent-detail'),
path('parents/<int:pk>/delete_child/<int:child_pk>/', views.ParentDeleteChildView.as_view(), name='parent-delete-child')
But I have no idea how to send the pk and the child_pk to the generic django DeleteView?!?!?!
class ParentDeleteChildView(DeleteView):
model = Child
success_url = reverse_lazy('myapp:parent-detail' kwargs={'pk':pk})
After deleting I want to go back to the parent detail page. But the success url needs to know the pk of the parent. How do I tell the generic view to delete the child that matches the child_pk and then go to the parent detail page that matches the pk? Am I better off not using the generic DeleteView?
Thanks in advance!
We can achieve it using get_success_url in django.
By default pk_url_kwarg is set to kwarg pk. But in this case we have to delete child object i.e child_pk. so, we have to mention it by overriding pk_url_kwarg to child_pk.
class ParentDeleteChildView(DeleteView):
model = Child
pk_url_kwarg = 'child_pk'
def get_success_url(self):
success_url = reverse_lazy('myapp:parent-detail' kwargs={'pk':self.kwargs['pk']})
return success_url

django. Passing id from one template to second template

I need to pass id from one template to another template. In template i am iterating over one model
{% for project in all_projects %}
<h3>{{ project.name }}</h3>
{% endfor %}
This going to one template where my url looks like
url(r'^$', views.ProjectsListView.as_view(), name='index'),
url(r'^platforms/$', views.PlatformsIndexView.as_view(), name='platforms'),
url(r'^platforms/nodes/$', views.PlatformsNodesListView.as_view(), name='platforms_list'),
Browser url that i have is http://127.0.0.1:8000/platforms/?project=1
that's ok good. But from second template i need to send third template another parametrs and filters. So how do i can get id of project?
I can not send now project id to third template because i am not iterating over it. How to remember id of project?
views.py
class ProjectsListView(ListView):
template_name = 'project/projects.html'
model = Project
context_object_name = 'all_projects'
class PlatformsIndexView(TemplateView):
template_name = 'project/platforms.html'
class PlatformsNodesListView(ListView):
template_name = 'project/general.html'
model = Platform
context_object_name = 'all_platforms'
def get_queryset(self):
queryset = super().get_queryset()
type_filter = self.request.GET.get('type')
project_filter = self.request.GET.get('project')
if type_filter in [Platform.BACKEND, Platform.ANDROID, Platform.IOS, Platform.FRONTEND]:
queryset = queryset.filter(type=type_filter)
if project_filter:
queryset = queryset.filter(project__id__exact=project_filter)
else:
raise Http404
return queryset
Please explain me.
Thank you in advance

Error Generic detail view must be called with either an object pk or a slug, even with the pk

I'm trying to update records of a view which has a foreign key field, due to this I'm getting an error, since I tried to update another model without a foreign key field and it worked very well.
There are other quetions like this, but in my case I'm passing the pk.
urls.py
urlpatterns = [
url(r'^info/(?P<studentpk>\d+)/update/$', views.updatestudent.as_view(), name="updatestudent"),
]
views.py
class updatestudent(UpdateView):
model = Student
form_class = forms.studentform
template_name = "temp/updatestudent.html"
def get_success_url(self):
return reverse("courses")
updatestudent.html
<form action="" method="post">{% csrf_token %}
{{ form.as_p }}
<input type="submit" value="Update" />
</form>
models.py
class Student(models.Model):
classfk = models.ForeignKey(Class)
name = models.CharField(max_length=100)
birth_date = models.DateField('Birthdate')
def __str__(self):
return self.name
error
AttributeError: Generic detail view updatestudent must be called with either an object pk or a slug.
Django doesn't expect you to use studentpk in the URL pattern. The easiest fix is to use pk instead.
url(r'^info/(?P<pk>\d+)/update/$', views.updatestudent.as_view(), name="updatestudent"),
If you really want to use studentpk, then set pk_url_kwarg in the view.
class updatestudent(UpdateView):
model = Student
form_class = forms.studentform
template_name = "temp/updatestudent.html"
pk_url_kwarg = 'studentpk'
Note that in Python, the recommended style is to name your class based view UpdateStudent, and your form class StudentForm.

Categories