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'),
Related
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()
My template structure is base.html where i included navbar.html inside the base
I have an app called tags and tags has a models.py and a views.py
inside the views.py, i have a django code as this
from tags.models import Tag
class TagList(ListView):
model = Tag
def get_queryset(self):
return Tags.object.all()
this works, when i call {{ object_list }} inside my template for the tag_list.html.
So i added the {{ object_list }} inside my template navbar.html which was included inside the base.html but it works only when am on the url that displays my tag_list.html and does not show anything when am on other urls or other templates..
How do I make it show irrespective of the template directory am inside or the url am displaying ... i want it to show
I've thought of copying my views.py code into every app view and repeat the same process for every templates but i have a lot of template directories i cannnot do this for all of them
If you need this variable for all pages you can use a context processor.
Otherwise a mixin class similar to ContextMixin could also do the job:
# views.py
class MyContextMixin:
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['tags_list'] = Tags.object.all()
return context
class TagList(MyContextMixin, ListView):
model = Tag
You can then extend each View as follows:
# other views.py
from tags.views import MyContextMixin
class OtherView(MyContextMixin, ...):
...
In both cases you add another context variable. Note that I renamed object_list to tags_list to not override the variable name used by ListView.
I have a generic Django view that renders a template. The template is in an app which other projects will use. Importing projects will typically subclass the View the app provides. The View has a default template, which does a job with generic wording.
99% of the time, subclassing Views will want to only change the text, so rather than make them duplicate the template for the sake of altering non-markup wording, i'm looking for a way to allow users of the class to replace wording in the template in the most efficient way.
Options explored so far:
template partials containing only the text which using apps can override (magic, a lot of user work)
A template_strings method on the view which provides a dict of strings which end up in the template context which subclasses can override
Using (abusing?) the translation system such that the app provides default english translations and using code can provide their own translations instead (not actually worked this one out yet, just an idea)
Doing the above template_strings through AppConfig, but this seems ... yucky like it may get very unweildy with a lot of English strings. If doing this I would create a context-like setup so you don't have to re-declare all strings
Seems like it should be a solved problem to subclass a view which does a complete job and just provide alternate strings for text. Is there a better method than the above? Convention? Something I am missing?
(django 1.11 Python 3.6.2)
You can either inherit TemplateView or add ContextMixin to your view, and then override the get_context_data function like this:
from django.views.generic import TemplateView
class BaseView(TemplateView):
template_name = "common.html"
class SubView(BaseView):
def get_context_data(self, **kwargs):
context = super(SubView, self).get_context_data(**kwargs)
context['content'] = "Some sub view text"
return context
Update: Use template overriding
If you want to separate the text out, this is the better way to go
To allow easily and DRY override template across apps, you might need to install this package (Some other detail here)
We define it similarly as above, but change the template_name instead:
from django.views.generic import TemplateView
class BaseView(TemplateView):
template_name = "main.html"
# on another app
class SubView(BaseView):
template_name = "sub_view.html"
Then the magic is you can extends and override block of the BaseView template like this:
base_app/templates/main.html
<p>I'm Common Text</p>
{% block main %}
<p>I'm Base View</p>
{% endblock %}
sub_app/templates/sub_view.html
{% extends "base_app:main.html" %}
{% block main %}
<p>I'm Sub View</p>
{% endblock %}
The result would be:
<p>I'm Common Text</p>
<p>I'm Sub View</p>
Afaik you covered the options pretty well. My example is probably just a variant of the the template strings but maybe it helps anyway...
class DefaultStringProvider():
TITLE = 'Hello'
DESCRIPTION = 'Original description'
CATEGORY = 'Stuff'
class MyTemplateBaseView(TemplateView):
def get_context_data(self, **kwargs):
return super(MyTemplateBaseView, self).get_context_data(
provider=self.get_string_provider(), **kwargs)
def get_string_provider(self):
return DefaultStringProvider()
class OtherView(MyTemplateBaseView):
template_name = 'welcome.html'
def get_string_provider(self):
p = DefaultStringProvider()
p.TITLE = 'Hello'
p.DESCRIPTION = 'New description'
return p
The idea is to have a default string provider and the base view populates the context with it through get_string_provider().
It will at least be quite clear which strings can be overridden for a user extending the base class and it will not interfere with translations.
I am asking user to fill extra fields with custom form. And in one of the fields, I have to let user choose multiple hierarchical tags. For this, I need to pass the tags from a view to the template signup.html
from classes.Tags import Tags
from django.shortcuts import render_to_response
from allauth.socialaccount import views as signup_views
def signup_view(request):
tags = Tags()
parameters={}
all_tags = tags.get_tags()
parameters['all_tags'] = all_tags
response = signup_views.signup(request)
return response
And in urls.py, I added this line before the allauth urls include line.
url(r'^accounts/social/signup/', 'mainapp.signup_views.signup_view', name = 'account_signup'),
url(r'^accounts/', include('allauth.urls')),
What I need is that I need to add all_tags to the response so that I can access it from the template. How do I do that?
This link has some details on using your own signup form. IMO, you can define your own form (eventually with a custom widget for the tags) and use it directly, without having to mess with the view.
Otherwise, #PauloAlmeida is correct. You could inherit a new class off SignupView with something like:
class MySignupView(SignupView):
def get_context_data(self, **kwargs):
ret = super(MySignupView, self).get_context_data(**kwargs)
ret['all_tags'] = Tags.get_tags()
return ret
I'd rather use the custom form approach as it won't mess up the urls.py.
I want to create custom page for admin panel without model. For first i copy index.html to project folder:
mysite/
templates/
admin/
index.html
Then add to apps block my code:
<div class="module">
<table summary="{% blocktrans with name="preferences" %}Models available in the preferences application.{% endblocktrans %}">
<caption>{% blocktrans with name="preferences" %}Preferences{% endblocktrans %}</caption>
<tr>
<th scope="row">Preferences</th>
<td>{% trans 'Change' %}</td>
</tr>
</table>
</div>
This works good, then I create new page /templates/admin/preferences/preferences.html and
add to urls.py:
url(r'^admin/preferences/$', TemplateView.as_view(template_name='admin/preferences/preferences.html')),
And add code to preferences.html:
{% extends "admin/base_site.html" %}
{% block title %}Test page{% endblock %}
Run it and see message with error "The requested admin page does not exist.". What I do wrong?
You need to add your admin URL before the URL patterns of the admin itself:
urlpatterns = patterns('',
url(r'^admin/preferences/$', TemplateView.as_view(template_name='admin/preferences/preferences.html')),
url(r'^admin/', include('django.contrib.admin.urls')),
)
This way the URL won't be processed by Django's admin.
Years go by and still a relevant answer to this can be posted.
Using Django 1.10+ you can do:
security/admin.py (this is your app's admin file)
from django.contrib import admin
from django.conf.urls import url
from django.template.response import TemplateResponse
from security.models import Security
#admin.register(Security)
class SecurityAdmin(admin.ModelAdmin):
def get_urls(self):
# get the default urls
urls = super(SecurityAdmin, self).get_urls()
# define security urls
security_urls = [
url(r'^configuration/$', self.admin_site.admin_view(self.security_configuration))
# Add here more urls if you want following same logic
]
# Make sure here you place your added urls first than the admin default urls
return security_urls + urls
# Your view definition fn
def security_configuration(self, request):
context = dict(
self.admin_site.each_context(request), # Include common variables for rendering the admin template.
something="test",
)
return TemplateResponse(request, "configuration.html", context)
security/templates/configuration.html
{% extends "admin/base_site.html" %}
{% block content %}
...
{% endblock %}
See Official ModelAdmin.get_urls description (make sure you select proper Django version, this code is valid for 1.10 above)
Note the use of get_urls() above.
This new admin page will be
accessible under:
https://localhost:8000/admin/security/configuration/
This page will be protected under admin login area
You should be using admin's get_urls.
If you want to create a custom page just to place there an arbitrary form to handle user input, you may give django-etc a try. There's etc.admin.CustomModelPage you can use:
# admin.py
from etc.admin import CustomModelPage
class MyPage(CustomModelPage):
title = 'My custom page' # set page title
# Define some fields you want to proccess data from.
my_field = models.CharField('some title', max_length=10)
def save(self):
# Here implement data handling.
super().save()
# Register the page within Django admin.
MyPage.register()
Here's an example of everything that should be needed (as of Django 1.6) for a custom admin page that is linked to from a button next to the "History" button in the top right of an object's detail page:
https://gist.github.com/mattlong/4b64212e096766e058b7
Full example:
from django.urls import path
from django.contrib import admin
from django.db import models
class DummyModel(models.Model):
class Meta:
verbose_name = 'Link to my shiny custom view'
app_label = 'users' # or another app to put your custom view
#admin.register(DummyModel)
class DummyModelAdmin(admin.ModelAdmin):
def get_urls(self):
view_name = '{}_{}_changelist'.format(
DummyModel._meta.app_label, DummyModel._meta.model_name)
return [
path('my_view/', MyCustomView.as_view(), name=view_name)
]
With this approach Django's makemigrations command will create DB migration to create table for DummyModel.
Extending the AdminSite class worked best for me, as per django's documentation. This solution protects the page(s) under the admin site login mechanism, and setting it up is easier than it may look:
Where you have other admin code (eg. myapp/admin.py), extend the default class:
from django.contrib.admin import AdminSite
class CustomAdminSite(AdminSite):
def get_urls(self):
custom_urls = [
path('admin/preferences/', self.admin_view(views.my_view)),
]
admin_urls = super().get_urls()
return custom_urls + admin_urls # custom urls must be at the beginning
site = CustomAdminSite()
# you can register your models on this site object as usual, if needed
site.register(Model, ModelAdmin)
Implement the view
def my_view(request):
return render(request, 'admin/preferences/preferences.html')
Use that admin site in urls.py, instead of the default one
from myapp import admin
# now use admin.site as you would use the default django one
urlpatterns = [
# ...
path('admin/', admin.site.urls),
# ...
]
If you want to hook a page into the existing admin site, then you can do the following, which is based on #arnaud-p's answer above. Arnaud's answer didn't work for me, as subclassing adminsite's get_url function lost access to existing admin pages until I added the registry as follows.
Using the following method, your additional pages will require staff access, and you don't need to change your urls.py, so this is great for making admin pages for apps etc... You can pass each_context in the view in order to get permissions etc.
works for django 3.2.9
In admin.py
from django.contrib import admin
from django.urls import path
from . import views
class CustomAdminSite(admin.AdminSite):
def get_urls(self):
self._registry = admin.site._registry
admin_urls = super().get_urls()
custom_urls = [
path('preferences/', views.Preferences.as_view(admin=self), name="preferences"),
]
return custom_urls + admin_urls # custom urls must be at the beginning
def get(self):
request.current_app == self.name
return super().get(request)
def get_app_list(self, request):
app_list = super().get_app_list(request)
app_list += [
{
"name": "My Custom Preferences App",
"app_label": "Preferences",
# "app_url": "/admin/test_view",
"models": [
{
"name": "Preferences",
"object_name": "preferences",
"admin_url": "/admin/preferences",
"view_only": True,
}
],
}
]
return app_list
site = CustomAdminSite()
the view...
class Preferences(views.generic.ListView):
admin = {}
def get(self, request):
ctx = self.admin.each_context(request)
return render(request, 'admin/preferences/preferences.html', ctx)
the template...
{% extends "admin/base_site.html" %}
{% block content %}
...HELLO WORLD!
{% endblock %}