I have the following model:
class Campaign:
...
class Location:
...
campaign = models.ForeignKey(Campaign)
I normally would edit the locations using a tabular inline, but because the number of Locations is very high I would like to move the management of the locations to another page. It should pretty much do the same as normal Admin page, except it should only display Locations for the selected campaign and when adding a new Location, it should be automatically attached to the right Campaign. To access the Location Admin page a link in the Campaigns list should be clicked.
What would be the right way to do this? Are there other options?
There is no out of the box solution for your problem, but it is pretty easy to do.
I would register Locations as a separate model admin object like this:
from django.contrib import admin
from models import Location
class LocationAdmin(EnhancedModelAdminMixin, admin.ModelAdmin):
list_filter = 'campaign',
admin.site.register(Location, LocationAdmin)
This way you will have admin interface where you can filter your locations by campaigns.
To have a direct access to Location on Campaign model admin I would use https://github.com/charettes/django-admin-enhancer
Auto selecting a filtered campaign on new Location create form it is a bit more trickier... But my approach would be to add a query parameter to Add new Location button and then catch it when initializing create form, to define initial value of field campaign.
First of all create a template on let's say your_app/admin/change_list.html, which looks like this:
{% extends "admin/change_list.html" %}
{% load i18n admin_static admin_list %}
{% load admin_urls %}
{% block object-tools %}
{% if has_add_permission %}
<ul class="object-tools">
{% block object-tools-items %}
<li>
<a href="{% url cl.opts|admin_urlname:'add' %}{% if is_popup %}?_popup=1{% if selected_campaign %}&selected_campaign={{ selected_campaign }}{% endif %}
{% elif selected_campaign %}?selected_campaign={{ selected_campaign }}{% endif %}"
class="addlink">
{% blocktrans with cl.opts.verbose_name as name %}Add {{ name }}{% endblocktrans %}
</a>
</li>
{% endblock %}
</ul>
{% endif %}
{% endblock %}
Then append your LocationAdmin class to look like this:
class LocationAdmin(EnhancedModelAdminMixin, admin.ModelAdmin):
list_filter = 'campaign',
list_display = 'location_name', 'campaign'
change_list_template = 'your_app/admin/change_list.html'
def changelist_view(self, request, extra_context=None):
if 'campaign__id__exact' in getattr(request, request.method):
selected_campaign = getattr(request, request.method)['campaign__id__exact']
if not extra_context:
extra_context = {'selected_campaign': selected_campaign}
else:
extra_context['selected_campaign'] = selected_campaign
return super(LocationAdmin, self).changelist_view(request, extra_context)
def formfield_for_foreignkey(self, db_field, request, **kwargs):
if db_field.name == 'campaign':
if 'selected_campaign' in getattr(request, request.method):
kwargs['initial'] = getattr(request, request.method)['selected_campaign']
return db_field.formfield(**kwargs)
return super(LocationAdmin, self).formfield_for_foreignkey(
db_field, request, **kwargs
)
So what we are doing here is we are overriding modeladmins changelist_view function just to add additional context variable of current 'selected_campaign'.
Then we adding it as a query parameter on Add new Location button.
Finaly we are using model admins formfield_for_foreignkey function to dynamically define initial value of campaign field based on parameter received with request when user clicks on Add new location.
Cheers! Do not forget to up-vote this answer if you feel it was helpful ;)
Related
So whenever I create a new user I would like to select a group (executive or employee).
I made these two groups in the Admin environment. Within the admin environment I can assign groups to users, how do I do this from a form (register.html)?
Do I have to query the groups in my forms.py? Or in models.py? I wonder how to do this.
Depending of the way you are using to render the html forms, you just need to pass the groups into the context so that you could use on the templates.
Example:
from django.contrib.auth.models import Group
def get_context_data(self, **kwargs):
context = super(NameOfYourView, self).get_context_data(**kwargs)
context['groups'] = Group.objects.all()
return context
In your template could try something like this:
{% block content %}
<h2>Publishers</h2>
<ul>
{% for group in groups %}
<li>{{ group.name }}</li>
{% endfor %}
</ul>
{% endblock %}
My requirement sounds simple, but I am struggling to make it work
In my wagtail project I have a BlogListingPage which has many child pages. I am trying to create a list of the children in a web page. Here is my code:
models.py
class BlogListingPage(Page):
...
def get_context(self, request, *args, **kwargs):
context = super().get_context(request, *args, **kwargs)
context['blog_pages'] = BlogDetailPage.objects.live().child_of(self)
return context
class BlogDetailPage(Page):
...
blog_listing = models.ForeignKey(BlogListingPage,
on_delete=models.PROTECT,
blank=True,
null=True,)
views.py
def blog(request):
url = 'blog/blog_listing_page.html'
context = {'data': BlogListingPage.objects.all()}
return render(request, url, context)
blog_listing_page.html
{% block content %}
{% for blog_listing in data %}
{% for post in blog_listing.blogdetailpage %}
{{ post.blog_title }}
{% endfor %}
{% endfor %}
{% endblock content %}
I am lost in the fog of the django/wagtail pages/objects/querysets/all()/specific
Can someone please lead me to clear blue water?
A couple of suggestions regarding your approach:
Having a foreign key from BlogDetailPage to BlogListingPage is not the Wagtail way to organise parent-child relationships. All Page objects already have a tree hierarchy and you should rely on that, otherwise you lose all the built-in tree handling provided by Wagtail.
You should use the parent/subpage rules to specify how page types can be related to each other. In you case it would look something like:
class BlogListingPage(Page):
subpage_types = ['myapp.BlogDetailPage']
class BlogDetailPage(Page):
parent_page_types = ['myapp.BlogListingPage']
The get_context() method on BlogListingPage only runs when Wagtail is serving that specific page in a view. It doesn't run when you're iterating over these pages in your blog view. In order to access the children there, you need to do something like this:
{% for blog_listing in data %}
{% for post in blog_listing.get_children.live %}
{{ post.blog_title }}
{% endfor %}
{% endfor %}
i.e., you need to use the get_children() method on the page to obtain it's children. live() further filters this to exclude unpublished pages. Note that this will only work if the blog posts are children (in the Wagtail page tree sense) of the listing page. specific() isn't important here if you're only using the post URL and title - it is relevant if you want to display fields that are part of your model rather than the base Page model.
In my web, user and admin user both login from frontend. Now I want to do that some of URLs are accessed by public user only. Is_staff user not access that URL. How do I do that?
Updated:
Can I use any decorator for this?
If you want to use a decorator, you can use the user_passes_test. First define a test function that checks that the user is not a staff member.
def is_not_staff(user):
return not user.is_staff
You can change the function to check user.is_authenticated (user.is_authenticated() in Django <=1.9) as well, if you don't want anonymous users to be able to access the view.
Then use user_passes_test with your test function to decorate the view you wish to protect.
#user_passes_test(is_not_staff)
def non_staff_view(request):
...
You can simply inherit LoginRequiredMixin and create your own custom access mixin as below:
class AccessMixin(LoginRequiredMixin):
def dispatch(self, request, *args, **kwargs):
if not request.user.is_authenticated or request.user.is_staff:
return self.handle_no_permission()
return super().dispatch(request, *args, **kwargs)
Now you just need to inherit AccessMixin in your view as below:
class HomePageView(AccessMixin, TemplateView):
login_url = <login-url>
...
...
So anonymous user and staff user won't be able to access the content of the page.
You can also mention the same in your base template html
Consider you have created a base.html which extends content block and you can add permission as below:
Ex.
base.html
{% if user.is_authenticated %}
{% if user.is_staff %}
<h3>Not allowed to access this page</h3>
{% else %}
{% block content %} {% endblock %}
{% endif %}
{% endif %}
this way when you extend base.html in your template all the content you write within {% block content %} {% endblock %} will only be rendered for non-staff logged in user.
My URL
/tags/{slug}
points to this view:
class TagsDetailList(request, ListView):
queryset = Link.objects.filter(tags__name__in=['request.slug'])
template_name = "links/tags_detail_list.html"
So i have the request.slug object in the url.
Now i want to make a ListView that filters Link.objects by request.slug and respond to given template with the queried result.
All works but no queries are given on my template.
response template is:
{% extends "base.html" %}
{% block content %}
<h2>Tags Detail List</h2>
<ul>
{% if link in object_list %}
{% for link in object_list %}
<li>
{{ link.title }}
</li>
{% endfor %}
{% else %}
<p> Error: No Links in queryset! </p>
{% endif %}
<ul>
{% endblock %}
i dont get some elements, only the error message. its something bad on the request on my view.
who can help me and give me a hint how i can retrieve the request slug on my view?
EDIT:
nice solutions. i learned the way kwargs work(a small part).
But on the template i get still the error for no queryset. Tried both answers and also changed a bit but never really worked. Any hint what cause this?
If your urls pattern something like this:
r'^list/(?P<slug>[\w,\*]+)?$'
So in the views should be:
class TagsDetailList(ListView):
model = Link
template_name = "links/tags_detail_list.html"
def get_queryset(self):
qs = self.model.objects.all()
if self.kwargs.get('slug'):
qs = qs.filter(tags__name=self.kwargs['slug'])
return qs
What you've done here doesn't make sense: you're just asking for tags whose names are in the list consisting of the literal text " request.slug".
You need to override get_queryset so that it queries on the actual value of the slug, which is in self.kwargs.
def get_queryset(self, *args, **kwargs):
return Link.objects.filter(tags__name=self.kwargs ['slug'])
Also, I don't know what that if statement is doing in your template, but you haven't defined "link" do it will never evaluate to true, so no links will show.
Maybe it will help, but it works for me in Django 2+:
Your urls.py:
...
path('tags/<slug>/', TagsDetailList.as_view(), name='tags_detail'),
...
Your views.py:
...
class TagsDetailList(ListView):
model = Link
template_name = 'links/tags_detail_list.html'
def get_queryset(self, *args, **kwargs):
return Link.objects.filter(tags__slug=self.kwargs['slug'])
...
I am writing a django template Configuration_Detail.html and it renders correctly on the relevant urls. It does not, however, take any variables whatsoever from the view class. I had a very similar template Configuration_List.html which worked fine, though that was a ListView not a DetailView.
Configuration_Detail.html:
{% extends "base.html" %}
{% load i18n %}
{% block title %}{% trans 'MySite Database' %}{% endblock %}
{% block branding %}
<h1 id="site-name">{% trans 'MySite Database: Current Instrumentation Configuration' %}</h1>
{% endblock %}
{% block content %}
Here is some text {{name}} with a variable in the middle.
{% endblock %}
The page renders the title bar fine, but the content block becomes "Here is some text with a variable in the middle."
I believe it should be taking the variable {{ name }} from here.
views.py:
class ConfigurationDetail(DetailView):
model = Configuration
def getname(self):
name = 'debug'
return name
But it does not...
Any suggestions on how to fix this would be greatly appreciated.
Edited to add:
Models.py - Configuration:
class Configuration(models.Model):
title = models.CharField(max_length=100,unique=True,blank=False)
author = models.ForeignKey(User)
created = models.DateField("date created",auto_now_add=True)
modified = models.DateField("date modified",auto_now=True)
description = models.CharField(max_length=512)
drawing = models.ForeignKey(Drawing,blank=True,null=True)
instruments = models.ManyToManyField(Instrument)
def __unicode__(self):
return self.title
The get_context_data() method is using ctx['author'] = Configuration.author
For DetailView, an object variable is added in the context which points to the database object for which the view is being rendered. So in your template you can do:
{% block content %}
Here is some text {{ object.author.get_full_name }}
with a variable in the middle.
{% endblock %}
The get_full_name method is from the User object.
if i understood correctly you need to access a model property from within a template, but for that is sufficient to do {{ configuration.author }} without modifying the context data at all!
the DetailView puts in the context the selected model, accessible with the dot notation.