I am new to Django and have gone through the first Django App tutorial.I understand the separate bits but am not able to form a complete solution.
My requirement is to add a custom button to a Model's Change List Page in Admin, redirect it to a confirmation page, execute a function on confirmation and then redirect to the change list page with execution status.
I have added the custom button but am lost after that.
Currently this is what I am doing:
Changed the admin class list template
class ReconciliationAdmin(admin.ModelAdmin):
change_list_template = 'SalesReconciliation/change_list.html'
This is my change list template:
{% extends "admin/change_list.html" %}
{% load i18n admin_static %}
{% block object-tools-items %}
{{ block.super }}
<li>
Run Reconciliation
</li>
{% endblock %}
Changed the urls.py in the main app as follows:
urlpatterns = [
url(r"^reconcileConfirm/", include("SalesReconciliation.urls")),
]
Changed the urls.py in the child app as follows:
urlpatterns = [
url(r'^reconcileConfirm/', views.ReconcileConfirm, name='ReconcileConfirm')
]
In the views.py of the child app added the follows:
def ReconcileConfirm(request):
return HttpResponse("..something")
I am hoping that this would work like:
Click on Run Reconciliation link -> Main urls.py routes to child
urls.py -> child urls.py routes to views.py -> function in views py
performs some operations
But I am getting the following errors:
NoReverseMatch at /SalesReconciliation/reconciliation/ Reverse for 'reconcileConfirm' not found. 'reconcileConfirm' is not a valid view function or pattern name.
Request Method: GET
Request URL: http://127.0.0.1:8000/SalesReconciliation/reconciliation/
Django Version: 2.0.4
Exception Type: NoReverseMatch
Exception Value: Reverse for 'reconcileConfirm' not found.
'reconcileConfirm' is not a valid view function or pattern name.
Also I would like to know whether it is the correct approach.
In your template:
{% url 'admin:reconcileConfirm'%}
in urls.py:
urlpatterns = [
url(r'^reconcileConfirm/', views.ReconcileConfirm, name='ReconcileConfirm')
]
Note that ReconcileConfirm is different from reconcileConfirm. This is probably the first error to fix in order to achieve what you want to.
Related
I am having some issues directing a url pattern through the following type of path: Listview --> Listview2 --> DetailView. I am running into trouble with my url patterns. Here is what I am working with:
app_name = 'ism'
urlpatterns = [
path('', views.IndexView.as_view(), name='index'),
path('<slug:client_slug>/', views.CostCenterListView.as_view(), name='cost_center_list'),
path('<slug:client_slug>/<slug:cost_center_slug>/', views.cost_center_detail, name='cost_center_detail'),
]
The home page of this app is a list of all clients. Clicking on a client will populate a new page showing a list of that client's sub-clients (cost_center).
Here is part of my template {% url %} call to my final path listed above (/slug/slug/):
{% for cost_center in cost_centers %}
<ul>
<li>{{ cost_center }}</li>
</ul>
{% endfor %}
Adding this along with its accompanying view causes an error:
NoReverseMatch at /ism/cleint_slug/
Can you confirm that my issue has to deal with my {% url %} in my template not remembering the first slug in my url path? My error message seems to indicate that it's trying to find:
.../cost_center_slug
instead of:
.../client_slug/cost_center_slug/
My assumption is that django would magically remember the first part of my url pattern (ie client_slug), but that does not appear to be happening. Do I need to bring in more context variables in my view to allow for the calling of two url variables (also is url variable the right terminology? It doesn't sound right) in my template above?
Here is my full error message ('cffd' is a slug representing a cost-center):
Reverse for 'cost_center_detail' with arguments '('cffd',)' not found.
1 pattern(s) tried:
['ism/(?P<client_slug>[-a-zA-Z0-9_]+)/(?P<cost_center_slug>[-a-zA-Z0-9_]+)/$']
Here is my views.py. I was initially trying to work with a DetailView and its get_object method, but I couldn't get that to work in addition to the function based view I shown
class IndexView(generic.ListView):
template_name = 'ism/index.html'
context_object_name = 'client_list'
def get_queryset(self):
queryset = Client.objects.all()
return queryset
class CostCenterListView(generic.ListView):
template_name = 'ism/costcenter_list.html'
context_object_name = 'cost_centers'
def get_queryset(self):
slug = self.kwargs.get('client_slug')
client = Client.objects.get(slug=slug)
queryset = client.costcenter_set.all()
return queryset
def cost_center_detail(request, client_slug, cost_center_slug):
cost_center = get_object_or_404(CostCenter, slug=cost_center_slug)
context = {'cost_center': cost_center}
return render(request, 'ism/costcenter_detail.html', context)
I think you've got a wrong url. It should be either
{% url 'ism:cost_center_list' client_slug %}
or (you don't provide enough argument to construct cost_center_detail url)
{% url 'ism:cost_center_detail' client_slug cost_center.slug %}
Is there any way to add form (for example feedback form) to every page in CMS? I really like to use Wagtail FormBuilder so Editor guy can change fields.
My first idea is to create custom form page (inherited from AbstractEmailForm) as site root child and load it to base.html trough template tag. I can access page properties this way but I cant render the form.
Here is my template tag:
#register.assignment_tag(takes_context=True)
def get_feedback_form(context):
return context['request'].site.root_page.get_children().type(FeedbackFormPage).first()
And this is how I use it from base.html:
{% get_feedback_form as feedback_form %}
...
{{ feedback_form.specific.title }} <-- this works
{{ feedback_form.specific.form.as_p }} <-- this doesnt work
It would be nice somehow to create a form as snippet or add it to Site Settings, but I didnt find how to do that.
The main issue is how you are generating the form in the template with .form.as_p.
You will need to generate the form with the .get_form function, but you are best to do it within your template as the current user and page needs to be past in as arguments like this.
form = feedback_form_page.get_form(
page=feedback_form_page, user=request.user)
You can see how the form is built for the AbstractForm model here:
https://github.com/wagtail/wagtail/blob/master/wagtail/wagtailforms/models.py#L278
Full detailed example below, along with how you could work the form selection into the Site Settings module.
Link to a Form in Site Settings
Assuming you are referring to the Site Settings contrib module:
http://docs.wagtail.io/en/v1.13/reference/contrib/settings.html
The 'Edit Handlers' section of the documentation explains a great way to link to a page inside of your site settings.
http://docs.wagtail.io/en/v1.13/reference/contrib/settings.html?highlight=site%20settings#edit-handlers
Example (in models.py):
from wagtail.contrib.settings.models import BaseSetting, register_setting
# ...
#register_setting
class MyCustomSettings(BaseSetting):
feedback_form_page = models.ForeignKey(
'wagtailcore.Page', null=True, on_delete=models.SET_NULL)
panels = [
# note the page type declared within the pagechooserpanel
PageChooserPanel('feedback_form_page', ['base.FormPage']),
]
Once you set this model up, you will need to do makemigration and migrate for the changes to work in admin. You will then see inside the settings menu a sub-menu titled 'My Custom Settings'
Adding linked Form to every page
Add a block (so it can be overridden in templates) that has an include in your base template (eg. myapp/templates/base.html).
<!-- Footer -->
<footer>
{% block feedback_form %}{% include "includes/feedback_form.html" %}{% endblock feedback_form %}
{% include "includes/footer.html" %}
</footer>
Create an include template (eg. myapp/templates/includes/feedback_form.html)
{% load feedback_form_tags wagtailcore_tags %}
{% get_feedback_form as feedback_form %}
<form action="{% pageurl feedback_form.page %}" method="POST" role="form">
<h3>{{ feedback_form.page.title}}</h3>
{% csrf_token %}
{{ feedback_form.form.as_p }}
<input type="submit">
</form>
Build a Template Tag to get the form and page
Your template tag needs to build the form with the page's self.get_form() function. Eg. you your template tag (base/templatetags/feedback_form)
from django import template
from myapp.models import MyCustomSettings
register = template.Library()
# https://docs.djangoproject.com/en/1.9/howto/custom-template-tags/
#register.assignment_tag(takes_context=True)
def get_feedback_form(context):
request = context['request']
my_custom_settings = MyCustomSettings.for_site(request.site)
feedback_form_page = my_custom_settings.feedback_form_page.specific
form = feedback_form_page.get_form(
page=feedback_form_page, user=request.user)
return {'page': feedback_form_page, 'form': form}
This still works in wagtail 2.3 just need to replace
#register.assignment_tag(takes_context=True)
with
#register.simple_tag(takes_context=True) to conform with django 2.2
Also {% load feedback_form_tags wagtailcore_tags %} assumes your file inside of templates tags is named feedback_form_tags.py. I also added an __init__.py in the template tags folder although I'm not sure that was actually necessary.
I'm trying to (at the project level):
Use the default admin view (auth.views.login) as my login view
Set the admin.site.site_header within ulrs.py so that it appears on the both the login and admin pages
First, doing this does update the site_header on the admin page...
# urls.py
admin.site.site_header = "my header name"
urlpatterns = [
url(r'^admin/?', admin.site.urls),
url(r'^', include('django.contrib.auth.urls')),
...
]
From here it would appear I should be able to either do this:
# registration/login.html
{% extends "admin/login.html" %}
Or this (copy/paste from django/admin/templates/login.html):
# registration/login.html
{% extends "admin/base_site.html" %}
{% load i18n static %}
{% block extrastyle %}{{ block.super }}<link rel="stylesheet" type="text/css" href="{% static "admin/css/login.css" %}" />
{{ form.media }}
{% endblock %}
...
Neither employ the header with "my header name" like the admin view does.
Why doesn't my registration/login.html get the admin.site.site_header I'm setting in urls.py?
Thanks.
Update:
I see that auth/view.py doesn't have site_header in its context by default:
# auth/view.py
def login(...
current_site = get_current_site(request)
context = {
'form': form,
redirect_field_name: redirect_to,
'site': current_site,
'site_name': current_site.name,
}
...
And that admin/sites.py is responsible for defining AdminSite which sets site_header for the admin site
class AdminSite(object):
...
def each_context(self, request):
"""
Returns a dictionary of variables to put in the template context for
*every* page in the admin site.
For sites running on a subpath, use the SCRIPT_NAME value if site_url
hasn't been customized.
"""
script_name = request.META['SCRIPT_NAME']
site_url = script_name if self.site_url == '/' and script_name else self.site_url
return {
'site_title': self.site_title,
'site_header': self.site_header,
'site_url': site_url,
'has_permission': self.has_permission(request),
'available_apps': self.get_app_list(request),
}
...
I see how I could create my own base_site.html (hardcoding {{ "my header name }}) to use in the copy/pasted login.html above, but that seems messy. I'd like to set this variable in one place and use as much of admin directly as possible.
My high-level interest is to basically piggy-back the admin app for regular user accounts. Am I going about this wrong?
My high-level interest is to basically piggy-back the admin app for
regular user accounts. Am I going about this wrong?
Unfortunately, yes, you are.
https://docs.djangoproject.com/en/1.10/intro/tutorial02/
The admin isn’t intended to be used by site visitors. It’s for site
managers.
It's so easy to add an authentication system using one of the many tried and tested registrion/auth packages available (example django-allauth) that trying to re-purpose django admin as a user level app is wastefull and not very secure either.
I have an app that is going to display some information about people in my group. I'm getting a NoReverseMatch error when trying to use the url tag in my index.html. If I do not use the url tag, but specify the root, I do not receive the error.
The error says:
NoReverseMatch at /
Reverse for 'specialist' with arguments '(1,)' and keyword arguments '{}' not found. 1 pattern(s) tried: ['$(?P[0-9]+)/']
Here is are the urls.py files.
From the main wi_tech urls.py:
from django.conf.urls import include, url
from django.contrib import admin
urlpatterns = [
url(r'^$', include('person.urls')),
url(r'^tech/', include('person.urls')),
url(r'^admin/', admin.site.urls),
]
From the 'person' app urls.py:
from django.conf.urls import url
from . import views
app_name = 'person'
urlpatterns = [
url(r'^$', views.IndexView.as_view(), name='index'),
url(r'^(?P<pk>[0-9]+)/$', views.DetailView.as_view(), name='specialist'),
]
My views.py file looks like this:
from django.views import generic
from .models import Specialist
class IndexView(generic.ListView):
template_name = 'person/index.html'
context_object_name = 'person_list'
def get_queryset(self):
"""Return all specialists"""
return Specialist.objects.order_by('id')
class DetailView(generic.DetailView):
model = Specialist
template_name = 'person/detail.html'
And my index.html page looks like this:
{% if person_list %}
<ul>
{% for specialist in person_list %}
<li> {{ specialist }}</li>
{% endfor %}
</ul>
{% else %}
<p>No specialists are available.</p>
{% endif %}
If I change my tag in index.html to this, it works:
<li> {{ specialist }}</li>
Obviously this isn't an ideal situation in case the web root ever changes. I've reviewed a lot of SO questions on this, and nothing seems to match. I think the issue is the "$" in the beginning of the regex, but I don't see where that's coming from.
Specifically, I used this link as a really good reference point, but came up empty looking through my code.
what is NoReverseMatch and how do i fix it
Clearly there's something I'm missing.
Could it be the additional / at the end of the URL?
url(r'^(?P<pk>[0-9]+)/$', views.DetailView.as_view(), name='specialist')
The one after your primary key modifier. When you click the URL (with the (% url ':' model.name %)) what URL comes up in your browser?
I ended up scrapping this implementation, and going with a flatter structure whereby all models, views and templates are in the same application. I have not had this problem in the new design.
Im using the Django function for loggin out and it doesnt show my template. the codes here:
Views.py
class Logout(View):
#import pdb; pdb.set_trace()
template_name = ['cost_control_app/logout.html']
def get(self, request, *args, **kwargs):
return render(request, self.template_name)
def logout_view(request):
logout(request)
print ("ya")
return render(request, self.template_name)
Urls.py
urlpatterns = patterns(
'',
url(r'^logout/$', views.Logout.as_view(), name = "logout"),
)
And the template:
logout.html
{% extends 'base_cost_control.html' %}
{% block contentsubbase %}
<head>
<body>
<div>
<h2> Logged out succefully </h2>
<br>
Iniciar sesión
</div>
</body>
</head>
{% endblock contentsubbase %}
in my main page im using an a href layer to point a that url :
Logout
but itś not working, it only redirects me to a complete empty page called logout and if i go back in my browser im still logged in.
define your template_name as string
template_name = "cost_control_app/logout.html"
Your template should be place in your project app_folder/templates/app_name
You can also define template_dirs in settings.py as follows
TEMPLATE_DIRS = (
os.path.join(BASE_DIR, 'templates'),
)
now create a folder in your project named templates and put your templates on each app in a folder correspond to app_name
Step 3 is not necessary and is just an alternate
Django 3.1: If you don't need to control the logout process, you can let Django's standard authentication system take care of it. No need to write a view or modify urlpatterns, simply direct the user to a template you specify in the GET request:
Logout now
This assumes you have the default settings ('django.contrib.auth' in installed apps, 'django.contrib.auth.middleware.AuthenticationMiddleware' in middleware, and 'django.contrib.auth.context_processors.auth' in template context processors) in settings.py.