django: generic delete view - python

I want to build a generic delete view for my application. Basically I want to have the same behaviour as the standard django admin. E.g. I want to be able to delete different objects using the same view (and template).
I was looking on django docs, and looks like that DeleteViews are coupled with the models they are supposed to delete. E.g.
class AuthorDelete(DeleteView):
model = Author
success_url = reverse_lazy('author-list')
And I want to create something more generic, e.g.
class AnyDelete(DeleteView):
model = I want to have a list of models here
success_url = reverse_lazy('some-remaining-list')

The reason CBVs were invented were to solve problems like yours. As you have already written, to create your own delete view you just need to subclass DeleteView and change two properties. I find it very easy and do it all the time (the only non-dry work that I have to do is to hook it to urls.py).
In any case, if you really want to create something more generic (e.g only one view to delete every kind of model) then you'll need to use the content types framework. As you will see in the documentation, the content types framework can be used to work with objects of arbitrary models. So, in your case you can create a simple view that will get three parameters: app_label, model and pk of model to delete. And then you can implement it like this:
from django.contrib.contenttypes.models import ContentType
from django.shortcuts import get_object_or_404
def generic_delete_view(request, app_label, pk):
if request.method == 'POST':
my_type = ContentType.objects.get(app_label=app_label, model=model)
get_object_or_404(my_type.model_class(), pk=pk).delete()
# here you must determine *where* to return to
# probably by adding a class method to your Models
Of course in your urls.py you have to hook this view so that it receives three parameters (and then call it like this /generic_delete/application/Model/3). Here's an example of how you could hook it in your urls.py:
urlpatterns = patterns('',
# ....
url(
r'^generic_delete/(?P<app_label>\w+)/(?P<model>\w+)/(?P<pk>\d+)$',
views.generic_delete_view,
name='generic_delete'
) ,
# ...
)
If you have a list of objects and want to get the app_label and model of each one in order to construct the generic-delete urls you can do something like this:
from django.core.urlresolvers import reverse
object = # ...
ct = ContentType.objects.get_for_model(object)
generic_delete_url = reverse('generic_delete', kwargs = {
app_label=ct.app_label,
model=ct.model,
pk=object.pk
})
# so generic_delete_url now will be something like /my_app/MyModel/42

Related

Control requests to view and template output in django

This is a view for get all the records in the EducationalRecord model:
def all_education_resume(request):
RESUME_INFO['view'] = 'education'
educations_resume = EducationalRecord.objects.all().order_by('-created_date')
template = 'resumes/all_resume.html'
context = {'educations_resume': educations_resume, 'resume_info': RESUME_INFO}
return render(request, template, context)
Now, if I want to write exactly this view for other models (like job resumes, research resumes , etc.),
I must another view one separately.
My question is:
How can I get a view for all these requests, so first check the URL of
the request and then do the relevant query? How can I control URL
requests in my views?
My other question is exactly the same as my first question,with this difference:
control view that must render in specific template.In other words,in
second question the ratio between the template and the view is instead
of the ratio of the view to the url or how to create a template for
multiple views (for example, for a variety of database resume
resumes, I have a template) and then, depending on which view render,
the template output is different.
I have implemented these two issues as follows:
I wrote a view for each of request!
In each view, I set the value of RESUME_INFO['view'], and then I've checked it in a template page and specified the corresponding template.
What is the best solution to these two questions?
How can I get a view for all these requests, so first check the URL of the request and then do the relevant query? How can I control URL requests in my views?
You can access request.path, or you can let the url(..)s pass a parameter with kwargs that holds a reference to the model for example, but this is usually bad design. Typically if you use different models, you will likely have to order these different as well, filter these differently, render these differently, etc. If not, then this typically indicates that something is wrong with the modeling.
You can however make use of class-based views [Django-doc], to remove as much boilerplate as posssible. Your view looks like a ListView [Django-doc], by using such view, and patching where necessary, we can omit most of the "boilerplate" code:
# app/views.py
from django.views.generic.list import ListView
class MyBaseListView(ListView):
resume_info = None
template = 'resumes/all_resume.html'
def get_context_data(self, *args, **kwargs):
context = super().get_context_data(*args, **kwargs)
context['resume_info'] = {'view': self.resume_info}
return context
In the individual listviews, you then only need to specify the resume_info and the model or queryset to render it with the 'all_resume.html' template, for example:
# app/views.py
# ...
class EducationalResumeView(MyBaseListView):
queryset = EducationalRecord.objects.order_by('-created_date')
resume_info = 'education'
class OtherModelView(MyBaseListView):
model = OtherModel
resume_info = 'other_info'
So we can here use inheritance to define common things only once, and use it in multiple views. In case we need to change something in a specific view, we can override it at that level.
In the urls.py, you define such view with the .as_view() method [Django-doc]. For example:
# app/urls.py
from django.urls import path
from app.views import EducationalResumeView, OtherModelView
urlpatterns = [
path('education/', EducationalResumeView.as_view()),
path('other/', OtherModelView.as_view()),
]

Saving a Form to a Database (Django Python)

In my Django app I have a form with an email and a text area that needs to go into the database but am struggeling to do so. At the moment I have 2 different solutions in my code:
OK, let me write this from the beginning. Maybe it is better if you do the below steps with me now for an exercise.
Now, the correct way to do the above task (I try to clear this subject a little more because I know that many other people will read this problem and they can learn from this too. And it is important to understand it for your future tasks.
If you want to create a Form and you know that you will want to save the submitted data from that Form to the database, then of course you should start the whole task with creating a Model, thus a table for that in the database.
So, first you create a Model (which you will call ie. “Questions” in this case, since you want to call your Form ie. QuestionForm, so it is better if your Model and table will be related to that with their names too).
So your Model will be in your models.py file:
from django.db import models
# Create your models here.
class Questions(models.Model):
contact_email = models.EmailField(max_length=60)
question = models.CharField(max_length=600)
Then you will create a ModelForm from this in your forms.py file the following way:
from django import forms
from django.forms import ModelForm, Textarea
from . import models
class QuestionForm(ModelForm):
class Meta:
model = models.Questions
fields = ['contact_email', 'question']
widgets = {
'question': Textarea(attrs={'cols': 40, 'rows': 20}),
}
Then you create your view function in your views.py file:
from django.shortcuts import render, redirect
from . import forms
from . import models
from django.http import HttpResponse
def get_question(request):
form = forms.QuestionForm()
if request.method == 'POST':
form = forms.QuestionForm(request.POST)
if form.is_valid():
form.save()
return redirect(‘success.html’) # or you redirect anywhere you want to
else:
form = forms.QuestionForm()
return render(request, 'contact.html', {'form':form})
And at this point you will create your urlpattern in your urls.py to call the get_question view function. It will look like the following:
from django.conf.urls import url
from basic_app import views
# app_name = 'basic_app' # Important for referencing urls in HTML pages(use your own app_name here). But for this example task this is not important.
urlpatterns = [
url(r'^$', views.home, name='home'),
url(r'^questions/', views.get_question, name="questions_page"),
]
I hope I did not confuse you more. If you do the above steps, it should work for you. And you can create as many Forms as you want with the above steps and saving them easily in the Database.
The only other thing you have to have to run the above, is your 'contact.html' page which you already have and you already created.
(do not forget to run: python manage.py migrate)
So, I hope that you see, in the above example you do not mismatch fields and field names, and you do not get confused about what to save where. Since the Model and the Form is working together and created together with the same field names.

Refactoring Django class-based views, clean up 18 repetitive classes.

https://github.com/AnthonyBRoberts/fcclincoln/blob/master/apps/story/views.py
I'm a little embarrassed to admit that this is mine. But it is.
class FrontpageView(DetailView):
template_name = "welcome_content.html"
def get_object(self):
return get_object_or_404(Article, slug="front-page")
def get_context_data(self, **kwargs):
context = super(FrontpageView, self).get_context_data(**kwargs)
context['slug'] = "front-page"
events = Article.objects.filter(slug="events")
context['events'] = events
return context
So this is a pretty normal class-based detail view in Django.
It's assigning a template, getting an Article object, and adding some things to the context_data.
Then I copied this class 17 times. Each time, there's a different template, and a different slug, and different stuff added to the context_data.
The idea is that there's a WYSIWYG editor for administrators to change the web content, and a user authentication system, to allow multiple people access to the site content. Basically, a super-simple CMS, so no one has to edit html to update the site.
But I really wish I could refactor this so I don't have these nearly identical 18 classes. Any suggestions on where I should start on this would be most welcome.
Squash all of your classes down to a single class that inherits from TemplateResponseMixin, as DetailView does, (also check out the SingleObjectTemplateResponseMixin) and override its get_template_names() method to return the template appropriate for the current situation.
A beautiful example of this being used is in the django-blog-zinnia project
def get_template_names(self):
"""
Return a list of template names to be used for the view.
"""
model_type = self.get_model_type()
model_name = self.get_model_name()
templates = [
'zinnia/%s/%s/entry_list.html' % (model_type, model_name),
'zinnia/%s/%s_entry_list.html' % (model_type, model_name),
'zinnia/%s/entry_list.html' % model_type,
'zinnia/entry_list.html']
if self.template_name is not None:
templates.insert(0, self.template_name)
return templates
Django will take that list of names and try each item to see if it exists in the templates folder. If it does, that template is used.
Update
After looking at your code a little more closely, perhaps something like this:
In your main urls.py
# convert each url
url(r'^$', FrontpageView.as_view()),
url(r'^history/$', HistoryView.as_view()),
url(r'^calendar/$', CalendarView.as_view()),
url(r'^news/$', NewsView.as_view()),
url(r'^visitors/$', VisitorsView.as_view()),
...
# to just
url(r'^(?P<slug>[\w\d/-]+)/$', SuperSpecialAwesomeView.as_view()),
# but, put this at the end of urls list after any routes that don't use this view
DetailView, after setting the class attribute model, will check to see if slug is in the url's kwargs and if it is, it will use the slug to do a model lookup just like what you are already doing: Article.ojects.get(slug=self.kwargs['slug'])
models.py
You could add a type field to your Article model. The type will specify what type of article it is. For example, your ChildrenView, YouthView, and AdultView could all have a type of music (since the templates are all music, I'm assuming that's how they are related).
ARTICLE_TYPE_CHOICES = (
(0, 'music'),
(1, 'weddings'),
(2, 'outreach'),
...
)
class Article(models.Model):
...
type = models.IntegerField(choices=ARTICLE_TYPE_CHOICES)
...
Then, in your views.py
class SuperSpecialAwesomeView(DetailView):
template_name = None
model = Article
def get_template_names(self):
slug = self.kwargs.get('slug', '')
templates = [
# create a template based on just the slug
'{0}.html'.format(slug),
# create a template based on the model's type
'{0}.html'.format(self.object.get_type_display()),
]
# Allow for template_name overrides in subclasses
if self.template_name is not None:
templates.insert(0, self.template_name)
return templates
Given an article instance with a type of music and a slug of ministry/children, Django will look for a template named ministry/children.html and a template named music.html.
And if you need to do some special stuff for other views (like you will probably need to for SermonsView), then subclass SuperSpecialAwesomeView
class SermonsView(SuperSpecialAwesomeView):
paginate_by = 2
queryset = Article.objects.order_by('-publish_date')
A quick approach I would think:
Add a template field in the model with a list of predefined template choices (those can be created dynamically).
Override the default DetailView methods, override the get_template_names method to assign the proper template to the view (if not available fallback, that can be done through a try: except:).
Apart from that you can alter the View behaviour with any kind of model flags.
This way you can have a single entry point for a model, rather than defining repeatable views all over the place.
I tend to keep a FrontPageView independent from other views though, for easiness and because it serves a different purpose.
If you need repeatable context entries, consider a context processor, if you need repeatable context entries for specific views consider Mixins.
Rarely I can find a places I need to use CBD.
You can refactor it like this:
def editable_page(slug):
return {
'context': {
'slug': slug
}
'template': 'mysupertemplates/{0}.html'.format(slug)
}
def frontpage(req):
return editable_page('frontpage')
def chat(req):
return editable_page('char')
def about(req):
return editable_page('about')

Django multi model best practice

Basically what I'm trying to achieve is a multi-model django app where different models take advantage of the same views. For example I've got the models 'Car' 'Make' 'Model' etc and I want to build a single view to perform the same task for each, such as add, delete and edit, so I don't have to create a seperate view for add car, ass make etc. I've built a ModelForm and Model object for each and would want to create a blank object when adding and a pre-populated form object when editing (through the form instance arg), with objects being determined via url parameters.
Where I'm stuck is that I'm not sure what the best way to so this is. At the moment I'm using a load of if statements to return the desired object or form based on parameters I'm giving it, which get's a bit tricky when different forms need specifying and whether they need an instance or not. Although this seems to be far from the most efficient way of achieving this.
Django seems to have functions to cover most repetitive tasks, is there some magic I'm missing here?
edit - Here's an example of what I'm doing with the arguments I'm passing into the url:
def edit_object(request, object, id):
if(object==car):
form = carForm(instance = Car.objects.get(pk=id)
return render(request, 'template.html', {'form':form})
What about using Class Based Views? Using CBVs is the best way in Django to make reusable code. For this example maybe it can be a little longer than function based views, but when the project grows up it makes the difference. Also remember "Explicit is better than implicit".
urls.py
# Edit
url(r'^car/edit/(?P<pk>\d+)/$', EditCar.as_view(), name='edit-car'),
url(r'^make/edit/(?P<pk>\d+)/$', EditMake.as_view(), name='edit-make'),
# Delete
url(r'^car/delete/(?P<pk>\d+)/$', DeleteCar.as_view(), name='delete-car'),
url(r'^make/delete/(?P<pk>\d+)/$', DeleteMake.as_view(), name='delete-make'),
views.py
class EditSomethingMixin(object):
"""Use Mixins to reuse common behavior"""
template_name = 'template-edit.html'
class EditCar(EditSomethingMixin, UpdateView):
model = Car
form_class = CarForm
class EditMake(EditSomethingMixin, UpdateView):
model = Make
form_class = MakeForm
class DeleteSomethingMixin(object):
"""Use Mixins to reuse common behavior"""
template_name = 'template-delete.html'
class DeleteCar(DeleteSomethingMixin, DeleteView):
model = Car
class DeleteMake(DeleteSomethingMixin, DeleteView):
model = Make
Just pass your class and form as args to the method then call them in the code.
def edit_object(request, model_cls, model_form, id):
form = model_form(instance = model_cls.objects.get(pk=id)
return render(request, 'template.html', {'form':form})
then just pass in the correct classes and forms in your view methods
def edit_car(request,id):
return edit_object(request, Car, CarForm, id)
each method knows what classes to pass, so you eliminate the if statements.
urls.py
url(r'^car/delete/(?<pk>\d+)/', edit, {'model': Car})
url(r'^make/delete/(?<pk>\d+)/', edit, {'model': Make})
views.py
def edit(request, id, model):
model.objects.get(id=id).delete()

piston-django how many methods should a single handler contain

I've been building a handler class for each method I want to map to the url file. Is my approach correct or wrong? because I don't seem to find a way to map a resource to a method they all map to an entire class.
Regards,
The documentations seems very clear https://bitbucket.org/jespern/django-piston/wiki/Documentation#!resources
from piston.handler import BaseHandler
from myapp.models import Blogpost
class BlogpostHandler(BaseHandler):
allowed_methods = ('GET',)
model = Blogpost
def read(self, request, post_slug):
...
Piston lets you map resource to
models, and by doing so, it will do a
lot of the heavy lifting for you.
A resource can be just a class, but
usually you would want to define at
least 1 of 4 methods:
read is called on GET requests, and
should never modify data (idempotent.)
create is called on POST, and creates
new objects, and should return them
(or rc.CREATED.)
update is called on PUT, and should
update an existing product and return
them (or rc.ALL_OK.)
delete is called on DELETE, and should
delete an existing object. Should not
return anything, just rc.DELETED.
Also https://bitbucket.org/jespern/django-piston/wiki/Documentation#!mapping-urls
In urls.py:
from django.conf.urls.defaults import *
from piston.resource import Resource
from mysite.myapp.api.handlers import BlogpostHandler
blogpost_handler = Resource(BlogpostHandler)
urlpatterns = patterns('',
url(r'^blogpost/(?P<post_slug>[^/]+)/', blogpost_handler),
url(r'^blogposts/', blogpost_handler),
)

Categories

Resources