I would like to add a Cancel button to the django default admin model editor to enable going back to previous page in case users decide to cancel editing/creating a model. One Option to do that will be extending the 'admin/submit_line.html' and add a Cancel button to it.
However, the default django 'admin/submit_line.html' template already includes a 'Close' button as shown in the code snippet below.
<div class="submit-row">
{% block submit-row %}
{% if show_save %}<input type="submit" value="{% trans 'Save' %}" class="default" name="_save">{% endif %}
...
{% if show_save_as_new %}<input type="submit" value="{% trans 'Save as new' %}" name="_saveasnew">{% endif %}
{% if show_save_and_add_another %}
<input type="submit" value="{% trans 'Save and add another' %}" name="_addanother">{% endif %}
...
{% if show_close %}{% trans 'Close' %}
{% endif %}
{% endblock %}
</div>
If I copied the above template and override the show_close variable to True, the Close button will be shown and closes the form as expected. But Isn't there a way to configure 'show_close' visibility from the models.py or admin.py classes?
override submit_line.html template with following content:
{% extends "admin/submit_line.html" %}
{% load i18n admin_urls %}
{% block submit-row %}
{{ block.super }}
{% if not show_close and adminform.model_admin.show_close_button %}
{% translate 'Close' %}
{% endif %}
{% endblock %}
and then in your admin class you can set field show_close_button (can choose anything you want) to True/False and close button will show/hide.
class MyAdmin(admin.ModelAdmin):
...
show_close_button = True
...
using adminform.model_admin.show_close_button you will get to that field in template.
You could define a custom AdminSite and override the method each_context to add show_close to all admin forms
def each_context(self, request):
context = super().each_context(request)
context['show_close'] = True
return context
Or you can override changeform_view on your model admins to set extra_context. You could have a base class that all the admins where you wanted this functionality inherited from
def change_view(self, request, object_id, form_url='', extra_context=None):
extra_context = extra_context or {}
extra_context['show_close'] = True
return super().change_view(self, request, object_id, form_url=form_url, extra_context=extra_context)
I was able to change these variables by overriding the ModelAdmin.changeform_view method:
class UserAdmin(ModelAdmin):
def changeform_view(self, request, object_id=None, form_url='', extra_context=None):
extra_context = extra_context or {}
extra_context["show_save"] = False
extra_context["show_save_and_continue"] = False
extra_context["show_close"] = True
return super().changeform_view(
request,
object_id=object_id,
form_url=form_url,
extra_context=extra_context
)
Related
I'm trying to make Todo app. I want to make checkbox next to task, so when you select it, the task is set to done. The problem is, I can't really know how to change value of my BooleanField in Task Model. There are plenty of posts like this, but they are usually using functions inside views.py or use forms, but I can't relate do my form in ListView.
views.py
class TodolistView(LoginRequiredMixin, ListView):
model = Todolist
template_name = 'todolist.html'
def get_queryset(self):
return Todolist.objects.all().filter(user=self.request.user)
def get(self, request, *args, **kwargs):
todolist_objects = Todolist.objects.all()
return render(request, 'todolist.html', {'todolist_objects': todolist_objects})
todolist.html
{% extends 'base.html' %}
{% load crispy_forms_tags %}
{% block content %}
<p>Add new task</p>
{% for todo in todolist_objects %}
<div>
<form action="" method="post">
<li> {{ todo }} see details</l>
</form>
</div>
{% endfor %}
{% endblock %}
This may not be the best way of doing this (open to suggestions). But I want to display a button on my home page depending on the value of a Boolean in the custom user model.
I am passing the value of this boolean via context in the view. But I can't seem to get the template logic to work.
Models.py
from django.contrib.auth.models import AbstractUser
class CustomUser(AbstractUser):
isAdmin = models.BooleanField(default = False,)
#more models...
views.py
from django.views.generic import TemplateView
from django.contrib.auth import get_user_model
from accounts.models import CustomUser
class HomePageView(TemplateView):
template_name = 'home.html'
def get_context_data(self, **kwargs):
context = super(HomePageView, self).get_context_data(**kwargs)
if self.request.user.is_authenticated:
adminStatus = CustomUser.objects.get(id=self.request.user.id)
print(adminStatus.isAdmin)
context['adminStatus'] = str(adminStatus.isAdmin)
return context
home page template.html
{% extends 'base.html' %}
{% block body %}
{% if user.is_authenticated %}
<h4>Hi {{ user.username }}!</h4>
<a class="btn btn-primary btn-lg" href="{% url 'dashboard' %}" role="button"> Go to Dashboard</a>
{% else %}
<p>You are not logged in</p>
login
</div>
{% if adminStatus == "True" %}
<h1>test</h1>
<div class = "adminPanel">
<a class="btn btn-primary btn-lg" href="{% url 'newEquipment' %}" role="button"> add new equipment</a>
</div>
{% endif %}
{% endif %}
{% endblock %}
I can't see the "newEquipment" button even though the adminStatus context is equal to "True", as verified by the print() command.
I have a feeling my template logic is not correct. I also tried:
{% if adminStatus contains "True" %}
In the view, context['adminStatus'] is defined only when the user is logged in. Meanwhile in the template, you are checking for adminStatus when the user is not logged in.
First the return context statement needs to be un-indented once, so that context (with or without adminStatus) is available regardless:
def get_context_data(self, **kwargs):
context = super(HomePageView, self).get_context_data(**kwargs)
if self.request.user.is_authenticated:
adminStatus = CustomUser.objects.get(id=self.request.user.id)
context['adminStatus'] = adminStatus.isAdmin
return context
Next, yes you probably need to fix your template logic. Assuming you want to check for adminStatus only if the user is logged in, it should look like:
{% if user.is_authenticated %}
<h4>Hi {{ user.username }}!</h4>
...
{% if adminStatus %}
<h1>test</h1>
...
{% endif %}
{% else %}
<p>You are not logged in</p>
...
{% endif %}
Original answer:
In the view, you likely don't have to stringify adminStatus.isAdmin.
context['adminStatus'] = adminStatus.isAdmin
If passed to the context as a boolean, you should be able to use this expression in the template:
{% if adminStatus %}
I need to create a UpdateForm with a TemplateView. Why with TemplateView? Because, I has a attribute what is geo_location, and I'm using LeafLet maps, and LeafLet maps doesn't work with generic.UpdateView or others the same type.
Here my views from Update:
class UpdateStore(LoginRequiredMixin, TemplateView):
template_name = 'store_form'
success_url = reverse_lazy('register:store_list')
def post(self, request, *args, **kwargs):
store_id = kwargs['store']
store = get_object_or_404(Store, pk=store_id)
form = StoreForm(request.POST, on_edit=True)
if form.is_valid():
form.save()
return redirect(reverse('register:store_list'))
else:
context = self.get_context_data()
context['data_form'] = form
return render(request, self.template_name, context)
return self.get(request)
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
store_id = self.kwargs['store']
store = get_object_or_404(Store, pk=store_id)
data = {
'name': store.name,
'description': store.description,
'address': store.address,
'geo_loc': store.geo_loc,
'opened': store.opened
}
context['editing'] = True
context['data_form'] = StoreForm(initial=data, on_edit=True)
context['store'] = store
return context
Here is my template code:
{% extends 'base.html' %}
{% load bootstrap3 %}
{% load leaflet_tags %}
{% block extra_css %}
{% leaflet_css plugins="forms" %}
{% endblock %}
{% block body %}
<h1> Update Store </h1>
<form method="POST">
{% csrf_token %}
{{ form }}
{% buttons %}
<button type="submit">
{% bootstrap_icon "star" %} Save
</button>
{% endbuttons %}
</form>
{% endblock %}
{% block extra_js %}
{% leaflet_js plugins="forms" %}
{% endblock %}
I trying this, but in my template, the Forms doesn't load, and my template are blanked :(. Someone knows why? I need another method for get anything else?
Thanks.
The problem with your code is that you place the form in the data_form key of the context:
context['data_form'] = StoreForm(initial=data, on_edit=True)
and then on the template you try to use it with {{form}} instead of {{data_form}}. After that the form should be rendered.
I'm kind of puzzled with this task:
I have 2 tables: User, Codes
I want to generate randomly codes in a specific pattern.
I've already written that part as a function, but it's hard to implement the function
in the ModelAdmin.
So I would be very pleased if someone knows a trick to accomplish this.
It would be enough to have a button in the User form to envoke the function, which then creates these codes.
But how do I implement such a button?
Is there a way to to this?
EDIT: typo
SOLUTION:
Since I want to generate vouchers for a particular user I can edit the admin.py like this:
class MyUserAdmin(UserAdmin):
def vouchers(self, obj):
return "<a href='%s'>Generate vouchers</a>" % reverse(gen_voucher_view, kwargs={'user':obj.pk,})
vouchers.allow_tags = True
list_display = (..., 'vouchers')
which represents a clickable link in the admin view of my User model.
Now I connect the link to my view in urls.py by adding
url(r'admin/gen_vouchers/(?P<user>\w+)/$', gen_voucher_view, name='gen_voucher_view')
to urlpatterns.
For creating the vouchers I provide a form in forms.py
class VoucherGeneratorForm(forms.Form):
user = forms.CharField(User, required=True, widget=forms.HiddenInput())
amount = forms.IntegerField(min_value=0, max_value=500, required=True)
readonly = ('user', )
In views.py I'm adding my view function:
#login_required
def gen_voucher_view(request, user):
if request.method == 'POST': # If the form has been submitted...
form = VoucherGeneratorForm(request.POST) # A form bound to the POST data
if form.is_valid(): # All validation rules pass
# GENERATE vouchers here by using form.cleaned_data['amount']
# and user (generate_vouchers is a self defined function)
vouchers = generate_vouchers(user, form.cleaned_data['amount']
# set error or info message
if len(vouchers) == form.cleaned_data['amount']:
messages.info(request, "Successfully generated %d voucher codes for %s" % (form.cleaned_data['amount'], user))
else:
messages.error(request, "Something went wrong")
u = User.objects.get(pk=user)
form = VoucherGeneratorForm(initial={'user':user}) # An unbound form
return render_to_response('admin/codes.html', {'request': request, 'user':user, 'form':form, 'userobj': u}, context_instance=RequestContext(request))
else:
form = VoucherGeneratorForm(initial={'user':user}) # An unbound form
Last but not least create a template admin/codes.html where my form is displayed:
{% extends "admin/base_site.html" %}
{% load i18n admin_static static %}
{% block breadcrumbs %}
<div class="breadcrumbs">
{% trans 'Home' %}
›
{% trans 'Users' %}
›
{% trans 'Vouchercodes' %}
›
Voucher Generator
</div>
{% endblock %}
{% block extrastyle %}{{ block.super }}<link rel="stylesheet" type="text/css" href="{% static "admin/css/dashboard.css" %}" />{% endblock %}
{% block content %}
<div id="content-main">
{% if request.user.is_active and request.user.is_staff or userobj and userobj.is_active and userobj.is_staff %}
<h1 id="generator_title">Generate vouchers for {{user}}</h1>
<form id="formular_generator" action="" method="POST" enctype="multipart/form-data">
{% csrf_token %}
<table>{{ form }}</table>
<button id="generatebutton" type="submit" name="action" value="generate">Generate</input>
</form>
{% else %}
<p>{% trans "You don't have permission to access this site." %}</p>
</div>
{% endif %}
{% endblock %}
{% block sidebar %}
{% endblock %}
Done!
To export them in a pdf I used admin actions, as propsed by Sumeet Dhariwal below.
U mean that you need to run a script from within the admin ?
If so check out django-admin-tools
http://django-admin-tools.readthedocs.org/en/latest/dashboard.html
SOLUTION FOUND:
no that was not what i meant, because I want to generate vouchers for 1 particular user and not for more, but that's a good remark.
I want to replace dynamically "Site administration" by a custom string in my admin.
I've already overridden "base.html" for some other purpose, but now I need to pass a variable to this template to replace {{ title }} in
{% block content_title %}{% if title %}<h1>{{ title }}</h1>{% endif %}{% endblock %}
I've seen from this question that a variable can be passed to the change list template by overriding changelist_view and adding an extra_context in the model admin, but how can I pass an extra context to the "main" page of the admin"?
The index() view is inside django.contrib.admin.site.AdminSite class and supports extra_context as well, you could override it, something like:
def index(self, *args, **kwargs):
return admin.site.__class__.index(self, extra_context={'title':'customized title'}, *args, **kwargs)
admin.site.index = index.__get__(admin.site, admin.site.__class__)
Also you could override AdminSite directly and use customized_site instead of admin.site:
class CustomizedAdminSite(AdminSite):
def index(self, *args, **kwargs):
return super(CustomizedAdminSite, self).index(extra_context={...}, *args, **kwargs)
customized_site = CustomizedAdminSite()
If you want to have title in all Admin pages, better to use context processor or customize some template tag if you can.
You override the "admin/base_site.html" template:
{% extends "admin/base.html" %}
{% load i18n %}
{% block title %} {{ title }} | {% trans 'YOUR TITLE HERE' %} {% endblock %}
{% block branding %}
<h1 id="site-name">{% trans 'STUFF HERE PERHAPS' %} </h1>
{% endblock %}
{% block nav-global %}
{% endblock %}