Django ListView - python

It's my first time to use ListView and it doesn't work and give me error.
I put get_query but they still give me same error. How can I fix the problem?
And everytime when I write code in views.py I always used 'def' not 'class'. But could see many people use (and also django documents) 'class' for ListView. So for general render stuffs we use 'def' and for django.views.generic stuffs we use class right? Why they distinguished these two?
This is error what I got.
ImproperlyConfigured at /search/results
ListView is missing a QuerySet. Define ListView.model, ListView.queryset, or override ListView.get_queryset().
urls.py
from django.urls import path
from django.conf import settings
from django.views.generic import ListView, TemplateView
from . import views
app_name = 'search'
urlpatterns = [
path('', TemplateView.as_view(template_name = "search/index.html")),
path('results', ListView.as_view(template_name = 'search/results.html')),
path('beerlist', views.beerlist, name='beerlist'),
path('<int:beerinfo_id>', views.beerinfo, name='beerinfo'),
]
views.py
from django.shortcuts import render, get_object_or_404, redirect
from django.db.models import Q
from django.views.generic import ListView, TemplateView
from .models import Beerinfo
# Create your views here.
def index(TemplateView):
template_name = 'search/index.html'
def results(ListView):
model = Beerinfo
template_name = 'search/results.html'
def get_queryset(self):
query = self.request.GET.get('q')
object_list = Beerinfo.objects.filter(
Q(name__icontains = query) | Q(label__icontains = query)
)
return obejct_list
index.html
<form action="{% url 'search:results' %}" name="se">
<label for='search'>What do you want to find?</label>
<input type="text" name='q'>
<input type="submit">
</form>
results.html
<ul>
{% for beer in ojbect_list %}
<li>{{ beer.name }}</li>
{% endfor %}
</ul>
models.py
from django.db import models
# Create your models here.
class Beerinfo(models.Model):
name = models.CharField(max_length=100)
label = models.CharField(max_length=500)
def __str__(self):
return self.name

You need to define the class that the list view will work with. For example:
class UserListView(ListView):
model = User
You can use a function (def) to accomplish the same thing that a generic view class, the difference is that most of what you write in the function is already defined in the class. In my example above, that class already handles the rendering of a default template, a context with the list of object of that template and pagination. The idea is to keep your code DRY.
The second advantage is that it creates a standard for your code, for example the default template to be used is
%(app_label)s/%(model_name)s%(template_name_suffix)s.html, so if your app name is users and your model is User, the this view expects a template named users/userlist.html
To use the pagiation simply set the paginate_by attribute of the class.
If you are trying to implement a simple view (for example all CRUD actions, then is very likely that you will benefit from using clases. Another good thing that classes give you, is that you can inherit goodies, for example, you can create a BaseListView class that inherits from ListView and set paginate_by to 25. If all your clases inherit from BaseListView then all your list will be paginated by 25 elements.

In views.py change def to class , you need to define a class to use Listview, Class Results(ListView). In urls.py, you are calling Listview , you should call views.Results.as_view()

Related

Django - printing variables in templates

I created an app called "jobs", basically I'd like to create new "jobs" from the admin console and be able to post it on the jobs.html page.
I created the model and views but I think there is something wrong with the views that doesn't allow me to print the "jobs" on the html template.
Can you please tell me if the error is in views.py?
jobs/models.py
from django.db import models
# Create your models here.
class post_job(models.Model):
posizione= models.TextField(max_length=20)
descrizione= models.TextField(max_length=20)
requisiti= models.TextField(max_length=20)
def __str__(self):
"""String for representing the MyModelName object (in Admin site etc.)."""
return self.posizione
jobs/admin.py
from django.contrib import admin
from .models import post_job
# Register your models here.
admin.site.register(post_job)
jobs/views.py
from django.shortcuts import render
from .models import post_job
# Create your views here.
def viz_job(request):
posizione = post_job.posizione
print(posizione)
return render(request,'jobs/jobs.html',{'posizione':posizione})
Proper answer:
In your views:
from django.shortcuts import render
from .models import PostJob # proper naming
def viz_job(request):
jobs = PostJob.objects.all()
return render(request,'jobs/jobs.html',{'jobs': jobs})
in your template:
<ul>
{% for job in jobs %}
<li>
<h3>{{ job.posizione }}</h3>
<div>
{{ job.descrizione }}
</div>
</li>
{% endfor %}
</ul>
Note that all this is documented.
NB: if you're only interested in those two fields and don't need any of the model's methods, related objects or whatever, you can optimize the query a bit by using a values queryset that will yield dicts with the selected fields instead of full model instances:
jobs = PostJob.objects.values("posizione", "descrizione")
Everything else remains the same.
You have to know what do you want to return for the template, for example in the views.py :
from django.shortcuts import render
from .models import post_job
# Create your views here.
def viz_job(request):
jobs = []
descriziones = []
posizione = Job.objects.all()
for pos in posizione:
jobs.append(pos.posizione)
descriziones.append(pos.descrizione)
context = {
'posizione': jobs,
'descrizione': descriziones
}
return render(request, 'jobs/jobs.html',
context=context) # this will return context dictonary to the template
You can filter and get to fetch specific data from your database

Django ModelForm iterate through MultipleChoiceField Values and process on POST

Level: Absolute Beginner, trying to build an app to perform some db operation through web UI
models.py
from django.db import models
class MysqlUser(models.Model):
username = models.CharField(max_length=100)
password = models.CharField(max_length=50)
environment = models.CharField(max_length=50)
forms.py
from django import forms
from onboard_app.models import MysqlUser
class MysqlUserForm(forms.ModelForm):
CHOICES = (
('test', 'Test'),
('develop', 'Develop'),
('staging', 'Staging'),
)
environment = forms.MultipleChoiceField(choices=CHOICES)
password = forms.CharField(widget=forms.PasswordInput)
class Meta:
model = MysqlUser
fields = ('username', 'password', 'environment')
views.py
from django.shortcuts import render
from onboard_app.serializers import MysqlUserSerializer
from rest_framework import generics
from onboard_app.forms import MysqlUserForm
from onboard_app.models import MysqlUser
from django.views.generic.edit import CreateView, UpdateView, DeleteView
class MysqlCreateView(CreateView):
model = MysqlUser
form_class = MysqlUserForm
template_name = 'mysqluser_form.html'
success_url = '/mysql/user/add/'
mysqluser_form.html
{% extends "myapp/base.html" %}
{% block title %}MyApp{% endblock %}
{% block content %}
<h1>MySQL User Access</h1>
<form method="post">
{% csrf_token %}
{{ form.as_p }}
<input type="submit" class="btn btn-primary" value="Grant Access">
</form>
{% endblock %}
I'm trying to get Value(s) of field or MultipleChoiceFiled environment after the user Form Submit, and loop through the entered values to perform some action. I have been trying this for long, still can't figure out how. I don't want to process anything in the frontend html. I'm thinking it has to be processed in the Views but not sure how to get the values of the field and loop over.
Any examples or any django concepts to look into will help me a lot. Any help is appreciated, thanks in advance!
Unless you have already done so, I recommend going through Django docs on class-based views (https://docs.djangoproject.com/en/3.0/topics/class-based-views/intro/) to get an overview of how the whole thing works.
So after you submit a form, a post() method of your view is called. CreateView provides a default implementation, which validates the user input using the MysqlUserForm you have provided and then creates an instance of MysqlUser and redirects to the success_url.
If you want to add more logic, you need to overwrite the post() method (or some other method, called by post(), in particular, the form_valid method) and put your logic there. To get a complete sense of how things work, I recommend you to read through the CreateView, BaseCreateView, ProcessFormView, and ModelFormMixin source code, although the inheritance looks a bit complicated (and it is). More to that, I really advise you to walk through the request processing from the View.dispatch method all way to the form_valid method using the debugger and see how things really work. Trust me, it will really contribute to your development skills improvement and understanding. Actually, I've discovered the form_valid method to write this answer by reading the source code (I use rest-framework nowadays and don't remember a lot about Django views).
So, what you need is
class MysqlCreateView(CreateView):
model = MysqlUser
form_class = MysqlUserForm
template_name = 'mysqluser_form.html'
success_url = '/mysql/user/add/'
def form_valid(self, form):
environment = form.cleaned_data['environment']
# insert your code here
return super().form_valid(form)
P.S. A good IDE like a PyCharm is really much much more convenient to read source code, jump to relevant parts and debug than any text editor and PDB debugger.

How to call custom method in django's admin template

I've got a trouble: I can easily update my html template sending the "object_list" object and iterating it with 
{%for item in object_list%}
#... some operation
{% endfor %}
However I don't know yet to do the same with my overriden admin-template in "template/admin/add_form.html": how to send and retrieve a method I added to my admin.py ModelAdmin custom class?
here there's my url.py
from django.urls import path
from django.conf.urls import url
from . import views as mysearch_views
from django.contrib import admin
from django.views.generic import ListView, DetailView
from .models import MySearch
# Admin custom
admin.sites.AdminSite.site_header = ‘to be decided’
admin.sites.AdminSite.site_title = ‘to be decided’
admin.sites.AdminSite.index_title = ‘to be decided’
urlpatterns = [
url(r'^$', mysearch_views.my_search, name="my_search"),
url(r'^(?P<id>\d+)/(?P<slug>[\w-]+)/$', DetailView.as_view(model=MySearch, template_name="queries.html"), name="queries"),
url(r'^contacts', mysearch_views.contacts, name="contacts"),
url(r'^result-table', mysearch_views.create_table, name="results"),]
Thanks in advance
In your ModelAdmin class, you can override the add_view method to add custom context data for the Add Form. There are other methods for other admin views: change_view for the Change Form, etc.
def add_view(self, request, form_url='', extra_context=None):
extra_context = extra_context or {}
extra_context['your_custom_data'] = self.your_custom_method()
return super().add_view(request, form_url, extra_context=extra_context)
You can then use {{ your_custom_data }} in your overridden admin template.
add_view and the other related methods are documented here.

When I use the Django generic How can I Bring two models

I have two models : Advertisment and Banner
when I using "generic view" How can I Bring together at the same time
The code below bring only one Advertisment
urlpatterns = patterns('',
url(r'^(?P<pk>\d+)/$', DetailView.as_view(
model = Advertisment,
context_object_name = 'advertisment',
), name='cars-advertisment-detail'),
url(r'^$', SearchView.as_view(), name='cars-advertisment-search'),
)
Aidan's answer is good if you only want to do it for a single view, but if you want to show banners on each page automatically, you have two main options.
One is to create a template tag that renders the banner, and add this tag to your templates where you want banners to be shown.
Your tag could look like this:
#register.inclusion_tag('banner.html')
def banner_display():
random_banner = Banner.objects.order_by('?')[0]
return {'the_banner': random_banner}
Then, you would create a template that shows the banner:
<img src="{{ the_banner.url|safe }}" />
In your templates, where you need the banner, you would just say {% banner_display %}
The other option you have is to create a custom template context processor. This will inject your banner as a normal variable in all requests. This is perhaps even simpler:
def banner_display(request):
random_banner = Banner.objects.order_by('?')[0]
return {'the_banner': random_banner}
You should save this in a file and then add it to your TEMPLATE_CONTEXT_PROCESSORS setting. Now in every template you have a variable {{ the_banner }}.
You need to override the get_context_data() method of the class based view (as described in the docs).
from django.views.generic import DetailView
class YourDetailView(DetailView):
model = Advertisment
context_object_name = 'advertisment'
def get_context_data(self, *args, **kwargs):
context = super(YourDetailView, self).get_context_data(*args, **kwargs)
if 'banner_id' in self.kwargs:
context['banner'] = get_object_or_404(Banner, pk=self.kwargs['banner_id']
return context
I guess you'll need to update your url conf to include a primary key for the Banner model too.
from your_app.views import YourDetailView
url(r'^(?P<ad_pk>\d+)/(?P<banner_pk>\d+)/$', YourDetailView.as_view(), name='cars-advertisment-detail'),

how to send success message if we use django generic views

I am new to django (1.2.4). I have created some crud with generic views. But How can I show something like "The student was added successfully" when student is created using django's messaging framework?
As of Django 1.6+, using any class-based generic views, you can rely on the successMessageMixin. It's as simple as adding the mixin to your class definition and setting success_message attribute to whatever you want.
As Olivier Verdier mentioned, please remember to display messages in your main template!
a simple example from the docs:
from django.contrib.messages.views import SuccessMessageMixin
from django.views.generic.edit import CreateView
from myapp.models import Author
class AuthorCreate(SuccessMessageMixin, CreateView):
model = Author
success_url = '/success/'
success_message = "%(name)s was created successfully"
a more complex example:
from django.contrib.messages.views import SuccessMessageMixin
from django.views.generic.edit import CreateView
from myapp.models import ComplicatedModel
class ComplicatedCreate(SuccessMessageMixin, CreateView):
model = ComplicatedModel
success_url = '/success/'
success_message = "%(calculated_field)s was created successfully"
def get_success_message(self, cleaned_data):
# cleaned_data is the cleaned data from the form which is used for string formatting
return self.success_message % dict(cleaned_data,
calculated_field=self.object.calculated_field)
As far as I know, there isn't a straightforward way of doing this using traditional generic views. I've always felt that the documentation on generic views was pretty lacking and so never used them.
In theory you could use a decorator by making the assumption that a redirect meant a successful submission.
So you could write something like this (none of this code has been tested):
urls.py:
try:
from functools import wraps
except ImportError:
from django.utils.functional import wraps
from django.http import HttpRedirectResponse
from django.contrib import messages
from django.views.generic import *
def add_message(success_message=None):
def decorator(func):
def inner(request, *args, **kwargs):
resp = func(request, *args, **kwargs)
if isinstance(resp, HttpRedirectResponse):
messages.success(request, message)
return resp
return wraps(func)(inner)
return decorator
student_info_edit = {
'template_name': 'myapp/student/form.html',
'template_object_name': 'student',
'form_class': studentForm,
}
student_info_new = {
'template_name': 'myapp/student/form.html',
'form_class': studentForm,
'post_save_redirect': '/myapp/students/',
}
urlpatterns += patterns('',
url(r'^students/$', list_detail.object_list, { 'queryset': Student.objects.all() }, name="students"),
url(r'^students/(?P<object_id>\d+)/$', add_message("Student record updated successfully")(create_update.update_object), student_info_edit, name="student_detail"),
url(r'^students/new$', add_message("The student was added successfully.")(create_update.create_object), student_info_new, name="student_new"),
)
All that said and coded, Django 1.3 introduced class-based generic views, so if you're interested in moving onto Django 1.3 you should look into those. They may allow more customization, not sure.
In the long run I rarely see the benefit form using generic views, and this goes double for things like add/update.
The functionality that you are asking for is already implemented in Django generic views:
https://github.com/django/django/blob/1.2.X/django/views/generic/create_update.py#L115
You will see the messages by displaying messages in your main template.
Actually I think the documents explain it pretty well for generic/function based views:
https://docs.djangoproject.com/en/2.0/ref/contrib/messages/
It basically passes context to your template with an if statement to display that context or not.
View:
from django.contrib import messages
def home_page(request):
if request.method == 'POST':
messages.success(request, 'Student added successfully')
context = {}
return render(request, 'homepage/index.html', context)
else:
form =yourForm()
return render(request, 'homepage/index.html', form)
And then it will be displayed in your template using the following. Remember to iterate '...because otherwise the message storage will not be cleared for the next request':
{% if messages %}
<ul class="messages">
{% for message in messages %}
<li{% if message.tags %} class="{{ message.tags }}"{% endif %}>{{ message }}</li>
{% endfor %}
</ul>
{% endif %}
As it renders the page again just add an anchor tag to your form and include in your form action i.e.
action="{% url 'home_page:index' %}#subscribe"
If you're using bootstrap add class alert-success

Categories