I am trying to create a django template that has links to other pages (static images in particular, each with their own html template). I am trying to move away from hard-coding a URL and view for each one. Instead I want to capture them all with a general slug URL, and a view that takes the slug as input.
My slug URL in urls.py is working fine - when I manually input the slug field in the full URL it links to the correct template and directs me to the correct page. However l, when I try to reference any of links as slugs from the 'cside' template I keep getting the following error:
NoReverseMatch at /plots/cside
Reverse for '2E_C' not found. '2E_C' is not a valid view function or pattern name.
Basically, I want the 'cside' page to have links that are slugs.
Can anyone tell me what I am missing? I have tried everything!
Here is my urls.py:
from django.urls import re_path, path
from django.contrib.staticfiles.urls import staticfiles_urlpatterns
from . import views
from .views import mode
urlpatterns = [
re_path(r'^cside$', views.cside, name='cside'),
re_path(r'^lside$', views.lside, name='lside'),
re_path(r'^home$', views.home, name='home'),
#re_path(r'^2E_C$', views.m2E_C),
re_path(r'^4M_C$', views.m4M_C),
re_path(r'^6E_C$', views.m6E_C),
re_path(r'^6M_C$', views.m6M_C),
re_path(r'^8E_C$', views.m8E_C),
re_path(r'^2E_L$', views.m2E_L),
re_path(r'^4M_L$', views.m4M_L),
re_path(r'^6E_L$', views.m6E_L),
re_path(r'^6M_L$', views.m6M_L),
re_path(r'^8E_L$', views.m8E_L),
#re_path(r'^(?P<slug>[-\w]+)/$', views.mode, name='mode'),
path('<slug:slugIn>/', views.mode, name='mode')
]
Here is my views.py:
from django.views.generic import TemplateView, ListView
from django.http import HttpResponse, HttpResponseRedirect
from django.template import loader
from django.shortcuts import get_object_or_404, render
from django.urls import reverse
from django.views import generic, View
from .models import Mode
def cside(request):
template = loader.get_template('cside.html')
context = {}
return HttpResponse(template.render(context, request))
def lside(request):
template = loader.get_template('lside.html')
context = {}
return HttpResponse(template.render(context, request))
def home(request):
template = loader.get_template('home.html')
context = {}
return HttpResponse(template.render(context, request))
# Slug Solution
#======================================
def mode(request, slugIn=None):
model = Mode
#print(slugIn)
slugOut = Mode.objects.all()
#print(slugOut)
template = loader.get_template(slugIn+'.html')
context = {"slug": slugOut}
return HttpResponse(template.render(context, request))
# Hardcoded Old Solution
#======================================
# def m2E_C(request):
# template = loader.get_template('2E_C.html')
# context = {}
# return HttpResponse(template.render(context, request))
def m400M_C(request):
template = loader.get_template('4M.html')
context = {}
return HttpResponse(template.render(context, request))
def m6E_C(request):
template = loader.get_template('6E_C.html')
context = {}
return HttpResponse(template.render(context, request))
def m6M_C(request):
...
And here is the html template for the page I am having issues with:
<!DOCTYPE html
<html><head>
<style type="text/css">
a { color:#005fce; text-decoration:none; font-weight:normal;}
c { color:#000000; text-decoration:none; font-weight:bold;}
</style>
</head>
<body><div class="content">
<h1>C SIDE</h1><br>
Home<br><br>
<c>TYPES:</c><br>
2E<br>
4M<br>
6E<br>
6M<br>
8E<br>
I think my issue is that I can’t seem to pass the input slug field from the URL to the template file, to then know where to go.
Or maybe I need to use a model to save the slug, but I couldn't figure out how to do this either.
Your url is named mode so you need to change your link to use {% url ‘mode’ ‘2E_C’ %}.
Related
I created a small Django application to manage data that fits a simple a model. For now I only need two views: one to list all records and another to edit a record with a generic form. Everything functions as expected, except the redirection from the edit view upon a successful update. In urls.py are the following contents:
from django.urls import path
from . import views
app_name = 'reqs'
urlpatterns = [
path('', views.IndexView.as_view(), name='index'),
path('<int:pk>/', views.ReqUpdateView.as_view(), name='update'),
]
In forms.py:
from django.forms import ModelForm
from .models import Requirement
class RequirementForm(ModelForm):
class Meta:
model = Requirement
fields = ['name', 'priority', 'source' , 'rationale']
And the templeate requirement_form.html:
<h1>{{ requirement.id }} - {{ requirement.name }}</h1>
<form method="post" novalidate>
{% csrf_token %}
<table>
{{ form.as_table }}
<tr><td></td><td><button type="submit">Save</button></td></tr>
</table>
</form>
{% if error_message %}<p><strong>{{ error_message }}</strong></p>{% endif %}
<br><br>
Back to list
Finally views.py, on a first attempt to redirect the update to the list:
from django.views.generic import ListView, UpdateView
from django.urls import reverse_lazy
from .models import Requirement
from .forms import RequirementForm
class IndexView(ListView):
template_name = 'reqs/index.html'
context_object_name = 'requirements_list'
def get_queryset(self):
return Requirement.objects.order_by('subject')
class ReqUpdateView(UpdateView):
model = Requirement
form_class = RequirementForm
success_url = reverse_lazy('/')
With this formulation the Save button produces this error:
Reverse for '/' not found. '/' is not a valid view function or pattern name.
I also tried an empty string as argument to reverse_lazy, as well as the path name index, but a similar error message is produced.
On a second attempt I tried to redirect to the same page, redefining the get_success_url method to do nothing:
class ReqUpdateView(UpdateView):
model = Requirement
form_class = RequirementForm
context_object_name = 'requirement_update'
def get_success_url(self):
pass #return the appropriate success url
This returns a 404 error trying to redirect the browser to /reqs/1/None.
A third attempt to redirect to the form with the same record:
class ReqUpdateView(UpdateView):
model = Requirement
form_class = RequirementForm
context_object_name = 'requirement_update'
def get_success_url(self):
pk = self.kwargs["pk"]
return reverse("update", kwargs={"pk": pk})
Which complains about not finding the view:
Reverse for 'update' not found. 'update' is not a valid view function or pattern name.
How can I redirect success to a valid URL? It can either be the items list or the item update view, as long as it works.
There are few misconception that you did
reverse parameter should be as documented
URL pattern name or the callable view object
You have set namespace but you are not reversing with namespace as documented
So in your case
def get_success_url(self):
pk = self.kwargs["pk"]
return reverse("reqs:update", kwargs={"pk": pk})
reverse / reverse_lazy are used to get the url using view name or pattern name. If you want to use a url directly just write:
success_url = '/'
For the case of return reverse("update", kwargs={"pk": pk}) not working since you set app_name = 'reqs' you should be using return reverse("reqs:update", kwargs={"pk": pk}) instead.
from django.contrib import admin
from django.urls import path
from . import views
urlpatterns = [
path('', views.Index, name='Index'),
path('register/', views.register, name='register'),
path('login/', views.login, name='login'),
path('register/reg_done/', views.reg_done,),
]
Above is my urls.py. What I am trying is to get my reg_done page come up as after a user click on the submit button to save the regestration info. But its showing that this page not found.
I tried to change the path in form action as register/reg_done/. But then it showed the same error with register/register/reg_done.
In HTML I am giving form action the value"/reg_done", that's it.
Below is my views.py
from django.shortcuts import render
from django.http import HttpResponse
import sqlite3
# Create your views here.
def Index(request):
return render(request, 'index.html')
def register(request):
return render(request, 'register.html')
def login(request):
return render(request, 'login.html')
def reg_done(request):
name = request.POST.get('name')
mail = request.POST.get('mail')
phone = request.POST.get('phone')
psw = request.POST.get('psw')
pswr = request.POST.get('pswr')
all = [name, mail, phone, psw, pswr]
return render(request, 'reg_done.html', {'all':all})
I assume the register view handles the registration. In the form for the register.html file,
Do:
<form action="/register/reg_done" method="post">
your form fields here
</form>
if you want to go with pure HTML solution. If you want something more Django-ly, use the url template tag:
<form action="{% url 'reg_done' %}" method="post">
your form fields here
</form>
I'll advice the later to ensure you avoid "premium developer tears".
In views.py, add this to the top of the file:
from django.views.decorators.http import require_POST
Then edit the reg_done view to:
#require_POST
def reg_done(request):
name = request.POST.get('name')
mail = request.POST.get('mail')
phone = request.POST.get('phone')
psw = request.POST.get('psw')
pswr = request.POST.get('pswr')
all = [name, mail, phone, psw, pswr]
return render(request, 'reg_done.html', {'all':all})
Next, change the path for reg_done in urls.py to:
path('register/reg_done/', views.reg_done, name='reg_done'),
The problem lies in your usage of urls. You should reference the reg_done view as /register/reg_done instead of register/reg_done. The former treats it as an url relative to the domain name while the latter treats it as relative to the current page. That's why coming from the register view and going to the latter yields register/register/reg_done rather than what you want: register/reg_done.
In laravel, it's possible to have access to entire models with type-hinting the parameter with a model class. for example:
routes/web.php
Route::get('posts/{post}', function(\App\Post $post) {
/*
$post is now the model with pk in url and
if the pk is wrong it throws an 404 automatically
*/
});
How is that possible in django? (with functions as view)
def posts(request, post_id):
post = get_model_or_404(Post, pk=post_id)
# ...
The second line is completely boilerplate and so much repeated in my code.
You can make a custom path converter [Django-doc] for that. We can for example make model path converter:
# app/converters.py
from django.shortcuts import get_object_or_404
from django.urls.converters import IntConverter
class ModelConverter(IntConverter):
model = None
def to_python(self, value):
return get_object_or_404(self.model, pk=super().to_python(value))
def to_url(self, value):
if instanceof(value, int):
return str(value)
return str(value.pk)
def model_converter(model):
class Converter(ModelConverter):
model = model
return Converter
Then in your urls.py you can make model converters and register these once:
# app/urls.py
from app.converters import model_converter
from app import views
from app.models import Post
from django.urls import path, register_converter
register_converter(model_converter(Post), 'post')
urlpatterns = [
path('posts/<post:post>', views.post, name='post'),
# …
]
Then in the view, you obtain the Post object (given a post for that primary key exists) through the post parameter:
app/views.py
def post(request, post):
# post is here a Post object
# …
In your template, you can then just pass the Post object in the {% url … %} template tag, so:
<a href="{% url 'post' mypostobject %}">
I created a very simple example code using Django, but cannot get model value to be displayed on my page:
----------------------------- home/models.py
from django.db import models
class Home(models.Model):
msg = models.CharField(max_length=100)
#classmethod
def create(cls, msg):
home = cls(msg=msg)
# do something with the book
return home
home = Home.create("Hello World!")
------------------------------------home/views.py
from django.views.generic import TemplateView
from project.models import Home
class IndexView(TemplateView):
model = Home
template_name = 'home/index.html'
------------------------------------------ templates/home/index.html
{{ home.msg }}
this is a test page. I am expecting to see this....
------------------------------------------- urls.py
from django.conf.urls.defaults import patterns, include, url
from django.contrib import admin
from django.views.generic import TemplateView
admin.autodiscover()
urlpatterns = patterns('',
# Home pagetentacl.urls
url(r'^$', TemplateView.as_view(template_name='home/index.html')),
# Uncomment the admin/doc line below to enable admin documentation:
# url(r'^admin/doc/', include('django.contrib.admindocs.urls')),
# Uncomment the next line to enable the admin:
url(r'^admin/', include(admin.site.urls)),
)
-------------------------------------- result page on browser:
this is a test page. I am expecting to see this....
I don't want to have DB access for my example. I want my model returns "hello world" string. home.msg on index.html doesn't return anything. What is missing here?
You're not giving the template an instance of Home. You need to create one and pass it to the template as a Context, in the form {'msg': msg}.
EDIT: Adding some code
First of all, you should create your instance of home in your view. I've never used TemplateViews, so I'm going to use a regular view method instead.
def IndexView(request):
home=Home.create("Hello World!")
return render(request, 'index.html', {'home': home},)
As #Daniel rightly points out, you're not giving your template an instance of Home to work with.
If you want to use class-based views, subclass TemplateView and override get_context_data():
class IndexView(TemplateView):
template_name = "home/index.html"
def get_context_data(self, **kwargs):
context = super(HomePageView, self).get_context_data(**kwargs)
context["home"] = Home.create("Hello World!")
return context
And make sure your urls.py is using IndexView.as_view() - your version above is just referencing the generic TemplateView.
The fact that you added a model field to your subclass of TemplateView makes me think you're confusing it with DetailView. See the documentation for the difference.
I'm new to django. Problem: to see my homepage (that shows the default django login form)
i have to write the addess:
x.x.x.xxx:8001/login
I would like to access it directly through that:
x.x.x.xxx:8001
What i can see now if i digit x.x.x.xxx:8001, is the login page without the form.
I have:
in my urls.py:
urlpatterns = patterns('',
url(r'^$', 'license.views.index', name='auth_login'),
in my views.py
from django import template
from django.shortcuts import render_to_response
from django.views.decorators.vary import vary_on_cookie
#vary_on_cookie
def index(request):
return render_to_response('login.html', {
}, context_instance = template.RequestContext(request))
That is because you are not passing a login form instance to your form.
Unless you want to perform some custom stuff in your view, your best bet is to use Django's built-in login view. You can set a custom template for it:
url(r'^$', 'django.contrib.auth.views.login', {'template_name': 'login.html'})
See this page for more info: django.contrib.auth.views.login (v1.5) docs
Now, I see that you are using a decorator on your view, which implies that you do indeed want to perform custom stuff. In that case, I believe you'll want to use the django.contrib.auth.forms.AuthenticationForm. Try doing something like this in your view:
from django.contrib.auth.forms import AuthenticationForm
from django.shortcuts import render
from django.views.decorators.vary import vary_on_cookie
#vary_on_cookie
def index(request):
form = AuthenticationForm(data=request.POST or None)
return render(request, 'login.html', {
'form': form
})