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

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

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.

Django: How to autopopulate foreign key with the corresponding model class instance

Working on my first Django project and could use some help. I have 2 models (Decisions, Votes) linked by the foreign key called 'decision'. The template, vote_list.html, shows the user a list of decisions (generated by other users) that are contained in Decisions. The user taps a particular decision and is re-directed to a second template to vote on options pertaining to that decision. How do I autopopulate the foreign key 'decision' in Votes with the corresponding instance of Decision so that the second template, vote_form.html, displays the options for the decision they tapped on? I assume it's coded in views.py (I commented an attempt below that doesn't work), but how might it be done? Thank you!
urls.py
path('vote-list/', views.VoterView.as_view(), name='vote_list'),
path('vote-list/<pk>/vote-form/', views.VoteForm.as_view(), name='vote_form'),
models.py
class Decisions(models.Model):
custom_user = models.ForeignKey(CustomUser,
default=None, null=True,
on_delete=models.SET_NULL)
description = models.CharField(default="",
max_length=100, verbose_name="Decision
Summary")
class Votes(models.Model):
decision = models.ForeignKey(Decisions,
default=None, null=True,
on_delete=models.SET_NULL)
vote = models.CharField(default="", max_length=100,
verbose_name="Your vote")
views.py
class VoteForm(LoginRequiredMixin, CreateView):
model = Votes
form_class = VotingForm
template_name = 'users/vote_form.html'
def post(self, request, *args, **kwargs):
super()
form = self.form_class(data=request.POST)
if form.is_valid():
instance = form.save(commit=False)
# instance.decision = Decisions.description
instance.save()
return redirect('users:vote_list')
forms.py
class VotingForm(forms.ModelForm):
class Meta:
model = Votes
fields = ['vote']
vote_list.html
{% for item in Decisions %}
<tr>
<td>{{ item.description }}</td>
<td><a href="{% url 'users:vote_form' item.id
%}">Vote</a></td>
</tr>
{% endfor %}
vote_form.html
{# trying to display the corresponding
decision description here from vote_list.html # }}
{{ form.vote|as_crispy_field }}
I think this might solve your problem:
Add decision field in the voting form. This will display an option to select for which decision you need to save this Vote for.
If you don't want to allow users to change the Decision, you can mark the field as disabled. See this issue for more details on how to do that. Another alternative is to completely hide the field.
class VotingForm(forms.ModelForm):
class Meta:
model = Votes
fields = ['vote', 'decision']
Add initial value of the decision when instantiating the VotingForm. This will automatically set which decision is selected when displaying the form.
class VoteForm(LoginRequiredMixin, CreateView):
model = Votes
form_class = VotingForm
template_name = 'users/vote_form.html'
# Use this to pass 'pk' to your context in the template
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context.update({'pk': self.kwargs['pk'})
return context
def get_initial(self):
initial = super().get_initial()
initial.update({'decision': self.kwargs['pk']})
return initial
def get_success_url():
# Import reverse from django.urls
return reverse('users:vote_list')
Also, your form should probably be displayed like this in the HTML template: {% crispy form %}. This way all defined fields from the VotingForm class are rendered automatically.
<form method="post" action="{% url 'users:vote_form' pk %}">
{% crispy form %}
</form>

Django add hard-coded href links to an admin model's form view page

In Django's admin panel, how do I add hard-coded links to a Django model's form page (/add/ page). These are links to documentation that will never change. I want these links to appear every time on the form as a reference for the user to figure out what values to input into the fields.
Do I need: Custom field? Built-in field already? Modify the admin templates somehow? Add a helper function somewhere?
I'm not referring to the "change list" view; I am referring to the /change/ or /add/ page view when you go and add or edit an object within a model.
models.py
class DateRange(models.Model):
date_name = models.CharField(max_length=100)
adwords_name = models.CharField(max_length=100)
bingads_name = models.CharField(max_length=100)
def __str__(self):
return self.date_name
forms.py
class DateRangeAdminForm(forms.ModelForm):
class Meta:
model = DateRange
fields = '__all__'
admin.py
#admin.register(DateRange)
class DateRangeAdmin(admin.ModelAdmin):
form = DateRangeAdminForm
list_display = ['date_name', 'adwords_name', 'bingads_name']
Extending change_form.html could work -- it'll add links at the top.
Create this file in your namespaced templates directory, referred to here as "templates-dir"
templates-dir/admin/myapp/daterange/change_form.html:
{% extends "admin/change_form.html" %}
{% block object-tools %}
{{ block.super }}
<ul>
<li>
Adwords documentation
</li>
<li>
Bing ads documentation
</li>
</ul>
{% endblock object-tools %}
Relevant docs:
https://docs.djangoproject.com/en/2.0/ref/contrib/admin/#overriding-admin-templates
https://docs.djangoproject.com/en/2.0/howto/overriding-templates/#overriding-from-an-app-s-template-directory

Django get abstract model value in template

I have abstract models and it displays when item created. How can i get this variable and display in my template?
class BaseModel(models.Model):
class Meta:
abstract = True
_created = models.DateTimeField(auto_now_add=True)
class Toy(BaseModel):
name = models.CharField(max_length=250)
views.py
toys = Toy.objects.all()
index.html
{% for k in toys %}
{{k.created}}
{% endfor %}
I tried this one but it doesn't work.
It's not ideal, but if you only need the _created field, create an empty list in your view, and then populate it with the field for each instance. Then, return the new list as a context variable.
For example,
created_values = []
for value in Toy.objects.all():
created_values.append(value._created)
The above is not tested, but I believe it should work.

Django Detailview display properties of parent

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 }}

Categories