I have a scenario that looks like this:
Models
from django.db import models
from django.core.urlresolvers import reverse_lazy, reverse
class State(models.Model):
short_name = models.CharField('Abbreviation', max_length=2)
state = models.SlugField('State Name', max_length=20)
def __str__(self):
return self.state
def get_absolute_url(self):
return reverse('state_list', kwargs={'state': self.state})
class City(models.Model):
state = models.ForeignKey(State)
city = models.CharField('City', max_length=100)
class Meta:
verbose_name = 'City'
verbose_name_plural = 'Cities'
def __str__(self):
return self.city
class School(models.Model):
name = models.CharField(max_length=69, default='')
def __str__(self):
return self.name
class Meta:
verbose_name = 'School'
verbose_name_plural = 'Schools'
Views
from django.shortcuts import get_object_or_404, render
from django.views.generic import ListView, DetailView, CreateView, UpdateView, DeleteView
from django.core.urlresolvers import reverse_lazy
from .models import School, City, State
def reviews_index(request):
state_list = State.objects.all()
context = {'states': state_list}
return render(request, 'reviews/reviews_index.html', context)
def state_detail(request, state=None):
city_list = City.objects.order_by('city')
context = {'cities': city_list}
return render(request, 'reviews/state_detail.html', context)
def city_detail(request, state=None, city=None):
school_list = School.objects.all()
context = {'schools': school_list}
return render(request, 'reviews/city_detail.html', context)
URLs
from django.conf.urls import url, include
from . import views
app_name = 'reviews'
urlpatterns = [
url(r'^$', views.reviews_index, name='reviews_index'),
url(r'^(?P<state>[a-z]+)/$', views.state_detail, name='state_detail'),
url(r'^(?P<state>[a-z]+)/(?P<city>[a-z]+)/$', views.city_detail, name='city_detail'),
]
However, when I try to create a link from the state_detail template to the city_detail template, I get this error:
NoReverseMatch at /reviews/alabama/
Reverse for 'city_detail' with arguments '('', 'Auburn')' and keyword arguments '{}' not found. 0 pattern(s) tried: []
This is how I am linking in the template:
{% block main_content %}
<div class="content">
<div class="row">
{% if cities %}
{% for city in cities %}
<div class="medium-3 column">
{{ city.city }}
</div>
{% endfor %}
{% endif %}
</div>
</div>
{% endblock %}
Can someone please tell me that I am doing wrong and help me fix it. Thanks in advance.
The city_detail url pattern includes the state.state, so you need to include the state in the template context.
In your view, you can use get_object_or_404 to fetch the state using the slug.
def state_detail(request, state=None):
state = get_object_or_404(State, state=state)
city_list = City.objects.filter(state=state).order_by('city')
context = {'cities': `city_list`, 'state': state}
return render(request, 'reviews/state_detail.html', context)
Note that I've changed city_list, so that it only displays cities in the state you are viewing.
It's not a great idea to use the same variable name state for the instance and the slug. It would be a good idea to rename one of them, e.g. to state_obj or state_slug. If you do this, you'll have to make sure you update your urls, views and templates to be consistent.
Related
Good evening, Django is completely new for me and it's my first question here.
I'm trying to create a Webapp. I'm using Django, Python and MariaDB.
I created a project with two apps and I have a model for each app. In the fist one I used "slug" as field name and everything is working fine. In the second one I wanted to differentiate that field giving a different name (bk_slug) defined as SlugField. I tried to use the same kind of instructions lines and modifying them for the field name it seems not working. I cannot have the right URL (Class based ListView) and cannot access to the Class based DetailView....
Thanks in advance for your support.
models.py
from django.db import models
from django.conf import settings
from django.utils import timezone
from django.urls import reverse
# book masterdata
class Book(models.Model):
bk_title = models.CharField("Book Title", max_length=100,
primary_key=True)
bk_originaltitle = models.CharField("Book original Title",
max_length=100, blank=True)
bk_author = models.CharField("Author", max_length=50)
bk_editor = models.CharField("Editor", max_length=50)
bk_editiondate = models.CharField("Book Edition date",
max_length=30)
bk_recblocked = models.BooleanField("Book record blocked")
bk_creator = models.ForeignKey(
settings.AUTH_USER_MODEL, on_delete=models.PROTECT,
related_name='+', verbose_name="Created by")
bk_creationdate = models.DateTimeField("Creation date",
auto_now=True)
bk_modifier = models.ForeignKey(settings.AUTH_USER_MODEL,
on_delete=models.PROTECT,
related_name='+', verbose_name="Last modification by")
bk_modified = models.DateTimeField("Last modification date",
auto_now=True)
bk_slug = models.SlugField(max_length=100, allow_unicode=True,
blank=True)
def __str__(self):
return self.bk_title
def get_absolute_url(self):
return reverse('book_detail', kwargs={'slug': self.bk_slug})
urls.py
from django.urls import path
from dimlibrary.views import BookListView
from dimlibrary.views import BookDetailView
urlpatterns = [
path('', BookListView.as_view(), name='book_list'),
path('<slug:bk_slug>/', BookDetailView.as_view(),
name='book_detail'),
]
views.py
from django.shortcuts import render
# Create your views here.
from django.shortcuts import render
from django.utils import timezone
from django.views.generic.list import ListView
from django.views.generic.detail import DetailView
from dimlibrary.models import Book
# book views :
# List view
class BookListView(ListView):
model = Book
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
return context
# Details View
class BookDetailView(DetailView):
model = Book
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
return context
```html
{% extends 'base/base.html' %}
{% load static %}
{% block content %}
<h1>Books List</h1>
<ul>
{% for book in object_list %}
<li><a href="{{ dimlibrary.book.get_absolute_url }}">{{
book.bk_title }}</a> - {{ book.bk_author }} -
{{ book.bk_editor }}</li>
{% empty %}
<li>No book registred yet.</li>
{% endfor %}
</ul>
{% endblock content %}
In your DetailView, you need to specify the slug_field [Django-doc] as 'bk_slug':
class BookDetailView(DetailView):
model = Book
slug_field = 'bk_slug'
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
return context
In the model, you will need to slugify(…) [Django-doc] the name, so:
from django.utils.text import slugify
class Book(models.Model):
# …
def save(self, *args, **kwargs):
if not self.bk_slug:
self.bk_slug = slugify(self.title)
return super().save(*args, **kwargs)
I am using class based views to create a post. I have used get_absolute_url to go to the post page after clicking on post but it is giving an error of no reverse match.
this is my modelspy
from django.db import models
from django.conf import settings
from django.urls import reverse
# Create your models here.
class BlogPost(models.Model):
title = models.CharField(max_length = 50 , null=False,blank=False)
body = models.TextField(max_length = 5000 , null=False,blank=False)
date_published = models.DateTimeField(auto_now_add=True)
date_update = models.DateTimeField(auto_now_add=True)
author = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)
def __str__(self):
return self.title
def get_absolute_url(self):
return reverse('article-detail', args=(str(self.id)))
this is my urls.py:
urlpatterns = [
path('',views.home,name="home"),
path('home2/',HomeView.as_view(),name = "home2"),
path('article/<int:pk>',ArticleDetailView.as_view(),name = "article-detail"),
path('add_post/',AddPostView.as_view(),name="add_post"),
]
this is home2.html:
<ul>
{%for post in object_list %}
<li>{{post.title}}-{{post.author}}<br/>
{{post.body}}</li>
{%endfor%}
</ul>
this is views.py:
from django.shortcuts import render
from .models import *
from .forms import *
from django.views.generic import ListView,CreateView
from django.views.generic.detail import DetailView
# Create your views here.
def home(request):
return render(request,'post/home.html')
class HomeView(ListView):
model = BlogPost
template_name = "post/home2.html"
class ArticleDetailView(DetailView):
model = BlogPost
template_name = "post/article_details.html"
context_object_name = 'post'
class AddPostView(CreateView):
model = BlogPost
template_name = "post/add_post.html"
fields = '__all__'
I would suggest doing something like. I provide 2 methods.
1st method:
By using get_absolute_url
from django.urls import reverse
def get_absolute_url(self):
return reverse('post:article-detail', args=(self.id)) #post is the app_name
urls.py
url(r'^article/(?P<pk>\d+)/$', ArticleDetailView.as_view(), name='article-detail'),
home2.html
<ul>
{%for post in object_list %}
<li>
{{post.title}}-{{post.author}}<br/>{{post.body}}
</li>
{%endfor%}
</ul>
2nd Method:
By using template tag.Here get absolute url with help of template tag.
home2.html
{% load templatehelpers %}
<ul>
{%for post in object_list %}
<li>
{{post.title}}-{{post.author}}<br/>
{{post.body}}
</li>
{% endfor %}
</ul>
Here we use a new simple tag namely abs_url (absolute url) in templatehelpers.py (Template tag). And app_name is the name of the app.
templatehelpers.py
from django import template
from django.urls import reverse
register = template.Library()
#register.simple_tag
def abs_url(value, request, **kwargs):
return reverse(value,kwargs=kwargs)
If you change this
def get_absolute_url(self):
return reverse('article-detail', args=(str(self.id)))
to this
def get_absolute_url(self):
return reverse('home')
You will not get the error, but you will not either be redirected to the post you just posted.
So if your goal is to just get rid of this error, but don't care about where you get redirected afterwards, then this is the solution for you.
#UPDATE
I want to make dynamic multiple buttons that filtering objects, similar to website https://justjoin.it/brands
For now I've done filtering with Django-filter that allows users to filter companies by Type, City and is company open for Students. But this django-filter require page to be refreshed which means it's not dynamic updating. It looks like this
image 1
image 2
and this is functional, works excellent.
Then I've created simple API with django-rest-framework which is accesible on 127.0.0.1:8000/api/companies and looks like this
Image
and also works fine.
The next thing that I've made is adding jQuery and simple script to get data from API, when someone submit form with ID=submit (image below)
image
But now I've stucked because I dont know how to get Data from API and compare this to template. I'm asking for any suggetions because I couldnt find any good example.
My files (updated)
models.py
from django.db import models
from django.db import models
from django.utils import timezone
from django.utils.text import slugify
from django.core.validators import MinValueValidator
from multiselectfield import MultiSelectField
import django_filters
TYPES = (
('Startup', 'Startup'),
('Software House', 'Software House'),
....)
CITIES = (
('Warszawa', 'Warszawa'),
('Poznan', 'Poznan'),
....)
COMPANY_TECHNOLOGIES = (
('PHP', 'PHP'),
('js', 'JavaScript'),
....)
STUDENTS = (
('No', 'No'),
('Yes', 'Yes')
)
class Company(models.Model):
name = models.CharField(max_length=100, blank=False)
students = models.CharField(max_length=3, choices=STUDENTS)
type = models.CharField(max_length=15, choices=TYPES)
workers = models.PositiveIntegerField(validators=[MinValueValidator(1)])
city = models.CharField(max_length=15,choices=CITIES)
stack = MultiSelectField(choices=COMPANY_TECHNOLOGIES)
....
def save(self, *args, **kwargs):
self.slug = slugify(self.name)
super(Company, self).save(*args, **kwargs)
def publish(self):
self.published_date = timezone.now()
self.save()
def __str__(self):
return self.name
views.py
from django.shortcuts import render, get_object_or_404, redirect
from django.utils import timezone
from .models import Company
from .filters import CompanyFilter
from rest_framework import viewsets
from .serializers import CompanySerializer
# Create your views here.
def companies_list(request):
my_companies = Company.objects.all()
## filter by company type
type = request.GET.get('type')
if type:
my_companies = Company.objects.filter(type=type)
## filter by company city
city = Company.objects.all()
if city:
my_companies = Company.objects.filter(city=city)
## filter by company technologies
stack = request.GET.get('stack')
if stack:
my_companies = Company.objects.filter(city=city)
my_companies = my_companies.order_by('published_date')
return render(request, 'company/companies_list.html', {'my_companies': my_companies})
def comp_list(request):
f = CompanyFilter(request.GET, queryset=Company.objects.all())
return render(request, 'company/comp_list.html', {'filter': f})
##def brands(request, slug):
## brands = Company.objects.all()
##return render(request, 'company/comp_view.html', {'brands': brands})
def brands(request, pk):
brand = get_object_or_404(Company, pk=pk)
return render(request, 'company/comp_view.html', {'brand': brand})
comp_list.html
{% extends 'company/base.html' %}
{% block content %}
<div id="filter">
<form action="" method="get" id="submit">
{{ filter.form.as_p }}
<input type="submit"/>
</form>
{% for obj in filter.qs %}
{{ obj.name }}
<p>Image {% if obj.image != None %}
<img src="{{ obj.image.url }}">
{% endif%}</p>
<p>Icon {% if obj.icon != None %}
<img src="{{ obj.icon.url }}" width="30" height="30">
{% endif%}</p>
<br> Type: {{ obj.type }} City: {{ obj.city }} Stack: {{ obj.stack }}
<br />
<br>
{% endfor %}
{% endblock %}
serializers.py
from .models import Company
from rest_framework import serializers
class CompanySerializer(serializers.ModelSerializer):
class Meta:
model = Company
fields = "__all__"
filters.py
import django_filters
from .models import Company, COMPANY_TECHNOLOGIES
from django_filters import ChoiceFilter
class CompanyFilter(django_filters.FilterSet):
class Meta:
model = Company
fields = ['type', 'city', 'students']
def __init__(self, *args, **kwargs):
super(CompanyFilter, self).__init__(*args, **kwargs)
self.filters['type'].extra.update(
{'empty_label': 'All'})
self.filters['city'].extra.update(
{'empty_label': 'All'})
self.filters['students'].extra.update(
{'empty_label': 'All'})
ajax.js
$( "#submit" ).click(function(event) {
event.preventDefault();
$.ajax({
url: "http://127.0.0.1:8000/api/companies/",
method: 'GET',
success: function(data){
console.log(data)
},
error: function(error_data){
console.log("error")
console.log(error_data)
}
})});
Filtering is usually done via GET parameters.
In this example, if you add ?company_name=acme to the URL, only company names with acme in the name would be shown:
def companies_list(request):
my_companies = company.objects.filter(
published_date__lte=timezone.now())
company_name = request.GET.get('company_name', None)
if company_name:
my_companies = my_companies.filter(company_name__icontains=company_name)
my_companies = my_companies.order_by('published_date')
return render(request, 'company/index.html', {'my_companies': my_companies})
There's also django-filter to ease this stuff.
As for the SPA, this is beyond the scope of the question, but it should be done with an AJAX request and replacing the DOM element with the response of the view, or returning a JSONResponse and building the element with JavaScript.
Sorry is this is basic but I'm new at this
I am attempting to take the captured group from a url in the template, (the primary key of the story model) and then use that to filter the correct posts from the Post datbase, which it has a one(story) to many(Post) relationship with. I based the code of the docs: https://docs.djangoproject.com/en/2.0/topics/class-based-views/generic-display/#dynamic-filtering
But when I run it, I get the error:
FieldError at /story/1/
Cannot resolve keyword 'name' into field. Choices are: body, id, title, work, work_id
My code:
#views
from django.shortcuts import get_object_or_404, render
from django.views.generic import ListView, DetailView
from . models import Post, Story
class StoryListView(ListView):
model = Story
template_name = 'home.html'
class PostListView(ListView):
template_name = 'story_overview.html'
def get_queryset(self):
self.work_id=get_object_or_404(Post, name=self.kwargs['pk'])
return Post.objects.filter(work_id=self.work_id)
#urls
from django.urls import path
from . import views
urlpatterns = [
path('', views.StoryListView.as_view(), name='home'),
path('story/<int:pk>/', views.PostListView.as_view(), name='story_overview'),
]
#templates/home.html
{% extends 'base.html' %}
{% block content %}
{% for post in object_list %}
<h2>{{ post.title }}</h2>
<p>{{ post.description }}</p>
{% endfor %}
{% endblock content %}
#models
from django.db import models
class Story(models.Model):
title = models.CharField(max_length=200)
description = models.CharField(max_length=1500, default= "Description")
def __str__(self):
return self.title
class Post(models.Model):
title = models.CharField(max_length=200, default= "Title")
work = models.ForeignKey(Story,on_delete=models.CASCADE,)
body = models.TextField()
def __str__(self):
return self.title
If you filter using work_id then you should use the integer.
def get_queryset(self):
return Post.objects.filter(work_id=self.kwargs['pk'])
If you want to fetch the Story instance then you should call get_object_or_404 with Story, and filter on the pk field instead of name:
def get_queryset(self):
self.work=get_object_or_404(Story, pk=self.kwargs['pk'])
return Post.objects.filter(work=self.work)
Note I've renamed work_id to work because it's a model instance, not an id.
You are trying to get object from Post() model using name field. Is name field present in your Post() model?
Instead of doing this
self.work_id=get_object_or_404(Post, name=self.kwargs['pk'])
Do this
self.work_id=get_object_or_404(Post, id=self.kwargs['pk'])
Hope it helps!
I'm trying to subclass the YearArchiveView class-based view to show a list of articles published in a year, but the filtering doesn't work and example.com/2012 shows articles from all years.
Note that I do not want the view code in urls.py. Rather, I want the LogbookYearArchive wrapper to continue living in views.py.
models.py:
class Entry(models.Model):
KIND = (
('L', 'Link'),
('A', 'Article'),
)
title = models.CharField(max_length=200)
slug = models.SlugField(unique_for_date='pub_date')
kind = models.CharField(max_length=1, choices=KIND, default=1,
help_text="Is this a link to other content or an original article?")
url = models.URLField(blank=True, help_text="The link URL")
body = models.TextField(blank=True)
body_html = models.TextField()
content_format = models.CharField(choices=CONTENT_FORMAT_CHOICES,
max_length=50, default=1)
is_active = models.BooleanField(help_text=_("Tick to make this entry\
live (see also the publication date). Note that administrators\
(like yourself) are allowed to preview inactive entries whereas\
the general public aren't."), default=True)
pub_date = models.DateTimeField(verbose_name=_("Publication date"),
help_text=_("For an entry to be published, it must be active and its\
publication date must be in the past."))
mod_date = models.DateTimeField(auto_now_add=True, editable=False)
class Meta:
db_table = 'blog_entries'
verbose_name_plural = 'entries'
ordering = ('-mod_date',)
get_latest_by = 'pub_date'
def __unicode__(self):
return self.title
#models.permalink
def get_absolute_url(self):
"""Construct the absolute URL for an Entry of kind == Article."""
return ('logbook-entry-detail', (), {
'year': self.pub_date.strftime("%Y"),
'month': self.pub_date.strftime("%m"),
'slug': self.slug})
urls.py:
from __future__ import absolute_import
from django.conf.urls import patterns, include, url
from .models import Entry
from .views import LogbookYearArchive
urlpatterns = patterns('',
url(r'^(?P<year>\d+)/$',
view=LogbookYearArchive.as_view(),
name='archive-year'),
)
views.py:
from django.views.generic.list import MultipleObjectMixin
from django.views.generic import ArchiveIndexView, MonthArchiveView, YearArchiveView, DetailView
from django.core.urlresolvers import reverse
from .models import Entry
class LogbookYearArchive(YearArchiveView):
"""Yearly archives of articles"""
model = Entry
date_field = 'pub_date'
year_format='%Y'
make_object_list=True,
template_name = 'hth/archive_year.html'
allow_future = False
def get_context_data(self, **kwargs):
context = super(LogbookYearArchive, self).get_context_data(**kwargs)
# =todo: fix filtering by date which is not working
context['object_list'] = Entry.objects.filter(
is_active=True, kind='A').order_by('-pub_date', 'title')[:9999]
return context
archive_year.html:
{% block content %}
<h1 style="margin-bottom:1em;">Articles Published in {{ year|date:"Y" }}</h1>
{% for object in object_list %}
{% ifchanged %}
<h2 class="dateline datelinearchive">{{ object.pub_date|date:"F Y" }}</h2>
{% endifchanged %}
<p>
{{ object.title }}
</p>
{% endfor %}
{% endblock %}
Can you try this:
class LogbookYearArchive(YearArchiveView):
"""Yearly archives of articles"""
model = Entry
date_field = 'pub_date'
year_format='%Y'
make_object_list=True,
template_name = 'hth/archive_year.html'
allow_future = False
queryset = Entry.objects.filter(
is_active=True, kind='A').order_by('-pub_date', 'title')
I added a queryset attribute and removed get_context_data()
I had the same problem. All ArchiveViews were working except YearArchiveView. Solution was found in make_object_list property. It should be True
That's a cut from django code
if not self.get_make_object_list():
# We need this to be a queryset since parent classes introspect it
# to find information about the model.
qs = qs.none()