Add clickable link to admin page List_view - django

I want to add a clickable link to admin list_view for a specific app that will redirect me to a html file i created, I have created a view.py like this
from django.shortcuts import render
from . models import *
from django.contrib.auth.decorators import login_required
#login_required
def transaction_print(request, transaction_id):
transaction = Transaction.objects.get(id=transaction_id)
return render(request, 'report.html', {'transaction': transaction})
and my urls.py
from django.urls import path
from . import views
urlpatterns = [
# path('', views.transaction_print, name='transaction_print'),
path('transaction/<int:transaction_id>/', views.transaction_print, name='transaction_print'),
]
I created the report.html at (root_dir/calculator/templates/report.html)
Now im not sure how to make a method at models.py to return maybe my views as a link to be add to list_view, or what is the best approach to achieving that. Thanks!

I guess there is a mistake in your formulation.
From what you describe, you want to add a button on the change_view.
To do so, within your app template folder, create a file extended_changeform.html
in that file:
{% extends 'admin/change_form.html' %}
{% block object-tools-items %}
<li>My button label
</li>
{{ block.super }}
{% endblock %}
and in the admin class where you want to have the buttons:
class TransactionAdmin(ModelAdmin):
change_form_template = "extended_changeform.html"

Related

How can I print the url to the order id field of my django form?

I am doing a simple form site with Django. This is what my sites url is looks like: mysite.com/register/12345678
I want to print the part after the register (12345678) to the order id field. When someone goes this mysite.com/register/87654321 url then i want to print it.
How can i do that? These are my codes.(Currently using Django 1.11.10)
forms.py
from django import forms
from .models import Customer
from . import views
class CustomerForm(forms.ModelForm):
class Meta:
model = Customer
fields = (
'order_id','full_name','company','email',
'phone_number','note')
widgets = {
'order_id': forms.TextInput(attrs={'class':'orderidcls'}),
'full_name': forms.TextInput(attrs={'class':'fullnamecls'}),
'company': forms.TextInput(attrs={'class':'companycls'}),
'email': forms.TextInput(attrs={'class':'emailcls'}),
'phone_number': forms.TextInput(attrs={'class':'pncls'}),
'note': forms.Textarea(attrs={'class':'notecls'}),
}
views.py
from django.shortcuts import render
from olvapp.models import Customer
from olvapp.forms import CustomerForm
from django.views.generic import CreateView,TemplateView
def guaform(request,pk):
form = CustomerForm()
if request.method == "POST":
form = CustomerForm(request.POST)
if form.is_valid():
form.save(commit=True)
else:
print('ERROR FORM INVALID')
theurl = request.get_full_path()
orderid = theurl[10:]
return render(request,'forms.py',{'form':form,'orderid':orderid})
customer_form.html
{% extends 'base.html' %}
{% block content %}
<h1>REGÄ°STRATÄ°ON</h1>
<form class="registclass" method="POST">
{% csrf_token %}
{{ form.as_p }}
<button type="submit" class="btn btn-default">REGISTER</button>
</form>
{% endblock %}
urls.py
from django.conf.urls import url
from django.contrib import admin
from olvapp import views
urlpatterns = [
url(r'^admin/', admin.site.urls),
url(r'^thanks/$',views.ThankView.as_view(),name='thank'),
url(r'^register/(?P<pk>\d+)',views.guaform,name='custform'),
]
You have passed the value to your view as 'pk' so you can use that to set the the initial value:
views.py
form = CustomerForm(initial={'order_id': pk})
SamSparx is right, here's some additional information to help prevent such errors in advance:
urlpatterns = [
url(r'^admin/', admin.site.urls),
url(r'^thanks/$',views.ThankView.as_view(),name='thank'),
url(r'^register/(?P<pk>\d+)',views.guaform,name='custform'),
]
You are using regex to parse your path. Regex is generally speaking not recommended in this case according to the docs because it might introduce more errors and is harder to debug. (Unoptimized) RegEx also tends to be slower.
For simple use cases such as your path here, consider choosing the default pathing syntax as below:
urlpatterns = [
url('admin/', admin.site.urls),
url('thanks/',views.ThankView.as_view(),name='thank'),
url('register/<int:pk>',views.guaform,name='custform'),
]
You could of course also use string instead of int depending on your usage of pk.
Your paths do not all end with a slash consistently. This might impact your SEO and confuse users. See this and this.
Also your form is not imported .as_view() for some reason which could cause some problems.

Django-filters FilterView shows empty list on page load

So I want to link to a given page that has filters on it, and have it display every item in the table before I click search, and only stop displaying items when bad input is given. My problem is similar to the following problem, with a few differences. Empty result list on django-filter page startup
The differences being the poster's default behavior is my desired behaviour and I am using class based views, not functional views.
My urls:
from django.urls import path
from . import views
app_name = 'advising'
urlpatterns = [
path('', views.MyList.as_view(), name='MyList'),
]
my views:
from django.http import HttpResponse, HttpResponseRedirect
from django.shortcuts import render, get_object_or_404
from django.views import generic
from django.template import loader
from .models import *
from django_filters.views import FilterView
from .filter import *
class MyList(FilterView):
template_name = 'advising/MyList.html'
context_object_name = 'tables'
filterset_class = MyFilter
def get_queryset(self):
return Table.objects.order_by('Name')
my filter:
import django_filters
from .models import Table
class MyFilter(django_filters.FilterSet):
Name = django_filters.CharFilter(lookup_expr='icontains')
class Meta:
model = Table #The table this form will reference
fields = ["Name"]
my template:
<form method="get">
{{ filter.form.as_p }}
<button type="submit">Search</button>
</form>
{% if tables %}
<ul>
{% for table in tables %}
<li>{{table}}</a></li>
{% endfor %}
</ul>
{% else %}
<p>Nothing to see here!.</p>
{% endif %}
Is there any way to mimic the behavior of searching for an empty string when the page first loads?
To be very specific, I want the url advising/ to have the same behaviour as the url advising/?Name=
Right now advising/ always gives me an empty list
Finally found a post with the same issue I'm having (no idea why it was never coming up on Google) where the issue was solved. It's as simple as adding the line "strict = False" in my view.
This is the question I found that answered it for me:
Display all record on django-filter page startup

Reverse error in Django 1.10

I'm new to Django and slowly learning how it works. I just upgraded to 1.10 and part of my app stopped working. I know it is related to the changes made into Reverse. I have been reading and I cannot find exactly what I'm doing wrong. Almost everything works as it should with a couple of exceptions. The behavior is as follows:
1) On my app I load reservations/create, it works perfectly I can create my reservation
2) When I click create, the reservation is actually created and saved into the database, but the browser is sent to the wrong address. It gets sent to reservations/create instead of reservations/reservation number (for example reservations/2 where it shows its details) and shows a Reverse error (included in this post)
3) If I test reservations/2 for example, it shows that it was actually created.
4) Also if a go straight to reservations/ it should show a list of all the ones already create, but instead shows a Reverse error too.
I would really appreciate any help in understanding what I'm doing wrong.
Models.py
class Reservation(models.Model):
res_number = models.AutoField(primary_key=True)
date = models.DateField(default=datetime.date.today())
status = models.CharField(max_length=10,default="Created")
reservation_type = models.CharField(max_length=11,choices=shced_type_choices, default="rental")
aircraft = models.ForeignKey('aircraft.Aircraft')
renter = models.CharField(max_length=30,blank=False,null=False)
instructor = models.CharField(max_length=30,blank=True,null=False)
def get_absolute_url(self):
return reverse("reservations:detail", kwargs={"res_number": self.res_number})
Main urls.py
url(r'^reservations/', include('dispatch.urls', namespace='reservations')),
Dispatch.urls
from django.conf.urls import include, url
from django.contrib import admin
from .views import (
reservations_list,
reservations_detail,
reservations_edit,
reservations_dispatch,
reservations_close,
reservations_cancel,
reservations_create,
reservations_close,
)
urlpatterns = [
url(r'^$', reservations_list),
url(r'^(?P<res_number>\d+)/$', reservations_detail),
url(r'^(?P<res_number>\d+)/edit/$', reservations_edit),
url(r'^(?P<res_number>\d+)/dispatch/$', reservations_dispatch),
url(r'^(?P<res_number>\d+)/close/$', reservations_close),
url(r'^(?P<res_number>\d+)/cancel/$', reservations_cancel),
url(r'^create/$', reservations_create),
url(r'^close/$', reservations_close),
]
Views.py
from django.contrib import messages
from django import forms
from django.http import HttpResponse, HttpResponseRedirect
from django.shortcuts import render, get_object_or_404
from .forms import ReservationForm, CloseReservationForm
from .models import Reservation
def reservations_list(request):
queryset = Reservation.objects.all()
context = {
"object_list": queryset,
"title": "List of Reservations:"
}
return render(request, "dispatch/list.html", context)
def reservations_detail(request, res_number=None):
instance = get_object_or_404(Reservation, res_number=res_number)
context = {
"title": instance.renter,
"instance": instance,
}
return render(request, "dispatch/details.html", context)
def reservations_create(request):
form = ReservationForm(request.POST or None)
if form.is_valid():
instance = form.save(commit=False)
print(instance.aircraft.hobbs)
instance.save()
messages.success(request, "Reservation Created")
return HttpResponseRedirect(instance.get_absolute_url())
context = {
"form": form,
}
return render(request, "dispatch/create.html", context)
Details.html
{% extends "dispatch/base.html" %}
{% block head_title %}{{ block.super }} | {{instance.res_number}}{% endblock head_title %}
{% block content %}
<h1>Reservation for {{title}} on {{instance.date}}</h1>
Reservation Number: {{instance.res_number}}</br>
Date: {{instance.date}}</br>
Status: {{instance.status}}</br>
Reservation Type: {{instance.reservation_type}}</br>
Aircraft: {{instance.aircraft}}</br>
Renter's Name: {{instance.renter}}</br>
Instructor's Name: {{instance.instructor}}</br>
Expected Flight Hours: {{instance.expected_hours}} Hrs</br>
Actual Flown Hours: {{instance.flown_hours}} Hrs</br>
Reservation Created on: {{instance.created}}</br>
Last Updated on: {{instance.updated}}</br>
{% endblock content %}
Create.html
{% extends "dispatch/base.html" %}
{% block head_title %}{{ block.super }} | Create{% endblock head_title %}
{% block content %}
<h1>Create Reservation</h1>
<form method='POST' action=''>{% csrf_token %}
{{form.as_p}}
<input type="submit" name="Create Reservation">
</form>
{% endblock content %}
Reverse error screenshot
Your problem is your routes don't have names. So when you are using reverse('some_name'), you have to have such name defined. The name is detail in your case, so you want to do something like this (see the parameter name)
urlpatterns = [
url(r'^(?P<res_number>\d+)/$', reservations_detail, name='detail'),
]
Also please don't insert traceback as a screenshot. You see the link 'switch to copy-and-paste view'? Yeah, use that the next time.

In Django template: Check if user has permission to access view specified by name

Say I have a view that has been decorated with the user_passes_test decorator:
# myapp/views.py
from django.views.generic import TemplateView
from django.contrib.auth.decorators import user_passes_test
def has_perm1_or_perm2(user):
return user.has_perm('myapp.perm1') or user.has_perm('myapp.perm2')
#user_passes_test(has_perm1_or_perm2)
class MyView(TemplateView):
# my view code goes here
and I hook it up to a URL as follows:
# myapp/urls.py
from django.conf.urls import url
from . import views
urlpatterns = [
...,
url(r'^myview$', views.MyView.as_view(), name='myview'),
...
]
If I wanted to put a link to this view in a template, it would look something like:
Check out my view!
But I would like to be able to put some control into the template so that the above is only shown if the currently-logged-in user would be approved by the user_passes_test function specified for the view.
I.e. I'd like to have my template look something like:
{% if check_user_has_view_permission request.user 'myapp:myview' %}
Check out my view!
{% else %}
Nothing to see here...
{% endif %}
How can I make that happen?
Thanks!
You can write a custom template filter that will do this:
# myapp/templatetags/my_app_tags.py (probably need a better name...)
from django import template
from myapp.views import has_perm1_or_perm2
register = template.Library()
#register.filter
user_has_special_perms(user):
return has_perm1_or_perm2(user)
Then in your template:
{% load my_app_tags %}
{% if request.user|user_has_special_perms %}
Check out my view!
{% else %}
Nothing to see here...
{% endif %}
Alternatively you can check the individual permissions directly like so:
{% if perms.myapp.perm1 and perms.myapp.perm2 %}
(obviously this is less DRY).
EDIT - how can this be made more generic?
To make this more generic, you could do something like this. First create some sort of mapping of views to permissions, e.g.,:
# myapp/views.py
VIEW_PERMISSIONS = {
'MyView': has_perm1_or_perm2,
# etc for other views
}
Then modify the template filter like so:
from myapp.views import VIEW_PERMISSIONS
#register.filter
user_has_special_perms(user, view_class):
perm_func = VIEW_PERMISSIONS.get(view_class, None)
if perm_func is not None:
return perm_func(user)
return false
And then in the template:
{% if request.user|user_has_special_perms:"MyView" %}
I'm sure it is possible to achieve this without requiring an explicit mapping through VIEW_PERMISSIONS, using some kind of introspection on the view class itself.

How do I install an app that doesn't have models or views?

I am attempting to install and test django-honeypot from sunlightlabs using the provided templates.
the application does not come accompanied with models nor views and I am confused as to how I am supposed to get the app to work. I have attempted to play with the urls to call the templates.
my mysite/mysite/urls.py follows:
from django.conf.urls.defaults import *
urlpatterns = patterns('',
#url(r'^$', include('honeypot.urls')),
)
and my mysite/honeypot/urls.py follows:
from django.conf.urls.defaults import *
#from tagging.views import tagged_object_list
from django.conf.urls import patterns, include, url
from django.contrib.staticfiles.urls import staticfiles_urlpatterns
from django.shortcuts import render
from django.contrib import admin
urlpatterns = patterns('',
url(r'^$', render, dict(template_name='/home/mohrashak/attribute2/honeypot/templates/honeypot/webpage.html'), name='price'),
)
ROOT_URLCONF="honeypot.urls"
where webpage is
{% load honeypot %}
<form>
{% render_honeypot_field "field_name" %}
</form>
and my understanding is that something will be entered into the box be processed using the application. But there is no model. What am I missing?
You don't need to worry about models or views for django-honeypot. It is installed into the site-packages folder of your Python library so you don't have to write models/views/urls.py for you - these are all there in your pythonpath.
Make sure you read the online documentation for installing the app.
Here is the checklist:
Add honeypot to INSTALLED_APPS in settings.py;
Define HONEYPOT_FIELD_NAME which is the name to use for the honeypot field in your form. Try not to use 'honeypot' though as some bots will avoid it.
Make sure you add {% load honeypot %} in the top of your template using django-honeypot;
Then use the following tag in any form {% render_honeypot_field "field_name" %}
When your form is submitted - you can use the check_honeypot decorator from honeypot.decorators to check the value is present (or not by default) and correct.
Here is the example from the documentation to add to your view:
from honeypot.decorators import check_honeypot
#check_honeypot(field_name='hp_field_name')
def post_comment(request):
...
#check_honeypot
def other_post_view(request):
...
Edit1:
In response to your comment:
Yes;
Yes.
No - the nature of django-honeypot is that it prevents "form spam". So you have to have at least a form in your template. You should pass a form from a view to a template. More information in the documentation. I've written a near full example below.
Example of a contact form using django-honeypot
Note: This is untested.
This is an example that creates and shows a contact form (by going to a /contact/ URL) and then handles the form being submitted. Imagine we have used django_admin.py startapp contact_app
urls.py
Your urls file takes the /contact/ URL passes the request to our contact view.
from django.conf.urls import patterns, url
urlpatterns = patterns('',
url(r'^contact/$', 'contact_app.views.contact'),
)
contact_app/views.py
Your views file takes the request, and handles GET and POST requests. If the request is a GET it passes an empty form to the template. If a POST then it checks the form is valid and processes the form. We wrap it in the check_honeypot decorator to make sure it passes our django-honeypot test.
from contact_app.forms import ContactForm
from honeypot.decorators import check_honeypot
#check_honeypot
def contact(request):
if request.method == 'POST': # If the form has been submitted...
form = ContactForm(request.POST) # A form bound to the POST data
if form.is_valid(): # All validation rules pass
# Process the data in form.cleaned_data
# ...
return HttpResponseRedirect('/thanks/') # Redirect after POST
else:
form = ContactForm() # An unbound form
return render(request, 'contact.html', {
'my_form': form,
})
contact_app/forms.py
Here we specify the form fields that are going to be required and display in the template.
from django import forms
class ContactForm(forms.Form):
subject = forms.CharField(max_length=100)
message = forms.CharField()
sender = forms.EmailField()
cc_myself = forms.BooleanField(required=False)
templates/contact.html
From our view, we pass our form (called my_form) to our template and use django's templating language to render each field as a <p></p>. We also load the honeypot and insert the necessary field.
{% load honeypot %}
<form action="/contact/" method="post">{% csrf_token %}
{{ my_form.as_p }}
{% render_honeypot_field "field_name" %}
<input type="submit" value="Submit" />
</form>