Cannot resolve keyword u'slug' into field error in Django? - django

I am getting the following error:
FieldError at /blog/1/first-post/
Cannot resolve keyword u'slug' into field. Choices are: article, date, id, likes
Request Method: GET
Request URL: http://127.0.0.1:8000/blog/1/first-post/
Django Version: 1.6.2
Exception Type: FieldError
Exception Value:
Cannot resolve keyword u'slug' into field. Choices are: article, date, id, likes
My model:
class Article(models.Model):
title = models.CharField(max_length=20)
body = models.TextField()
image = models.ImageField(upload_to="/", blank=True, null=True)
slug = models.SlugField()
def save(self, *args, **kwargs):
if not self.id:
self.slug = slugify(self.title)
super(Article, self).save(*args, **kwargs)
def get_absolute_url(self):
return reverse('article_detail', kwargs={'slug':self.slug, 'id':self.id})
def __unicode__(self):
return self.title
class Detail(models.Model):
article = models.ForeignKey(Article)
date = models.DateField()
likes = models.IntegerField()
def __unicode__(self):
return "%s %s" % (self.article.title, self.likes)
def get_absolute_url(self):
return reverse('detail_article', kwargs={'id':self.id})
View:
class ArticleDetail(DetailView):
model = Detail
template_name = "article_detail.html"
context_object_name = "details"
def get_queryset(self):
print self.kwargs['slug']
a = Article.objects.get(slug=self.kwargs['slug'])
# print Details.object.get()
# print Detail.objects.filter(article__slug=self.kwargs['slug']) fails with same error
return Detail.objects.filter(article=a)
urls.py (this is inside by blog app):
urlpatterns = patterns('',
url(r'all$', ArticleList.as_view(), name='blog_all'),
url(r'^(?P<id>\d+)/(?P<slug>[-\w\d]+)/$', ArticleDetail.as_view(), name='article_detail'),
url(r'^detail/?(P<id?\d+)/$', DetailArticle.as_view(), name='detail_article'),
url(r'^create$', ArticleCreateView.as_view(), name='blog_create'),
)
Basically the detailView of an article instance will display the contents of detail model that has foreignkey relationship to article model. It is not the traditional way where the detail view of article instance displays that instance.
Template here:
{% extends "base.html" %}
{% block content %}
{% for detail in details %}
<p>{{ detail.article.title }}</p>
<p>{{ detail.date }}</p>
<p>{{ detail.likes }}</p>
{% endfor %}
{% endblock %}

overriding the get_object(self, queryset=None) method instead of DetailView get_queryset(self) is an easier solution
class ArticleDetail(DetailView):
model = Detail
template_name = "article_detail.html"
context_object_name = "details"
def get_object(self, queryset=None):
slug = self.kwargs['slug']
a_obj = Article.objects.get(slug=slug)
try:
d_obj = Detail.objects.get(article=a_obj)
except Detail.DoesNotExist:
d_obj = None
except Detail.MultipleObjectsReturned:
#select the apt object
return d_obj

Solution: you need to rename slug parameter in url to other name, or in your view set slug_url_kwarg some other value - not 'slug'
Explanation:
When you add to url, django tries to get object by slug and your model Detail has no slug field.
Link to django code: https://github.com/django/django/blob/master/django/views/generic/detail.py#L33
UPDATE
in SingleObjectMixin:
slug = self.kwargs.get(self.slug_url_kwarg, None)
...
elif slug is not None:
slug_field = self.get_slug_field()
queryset = queryset.filter(**{slug_field: slug})
so django gets slug from your url, tries to get slug field from Detail model and fails
Your view need to rewrite slug_url_kwarg attribute:
class ArticleDetail(DetailView):
model = Detail
template_name = "article_detail.html"
context_object_name = "details"
slug_url_kwarg = "not_slug" # this attribute
def get_queryset(self):
print self.kwargs['slug']
a = Article.objects.get(slug=self.kwargs['slug'])
# print Details.object.get()
# print Detail.objects.filter(article__slug=self.kwargs['slug']) fails with same error
return Detail.objects.filter(article=a)
but I think better way is to change to attribute in your url:
url(r'^(?P<id>\d+)/(?P<article_slug>[-\w\d]+)/$', ArticleDetail.as_view(), name='article_detail'),
and get article_slug from view kwargs

Related

Displaying get_context_data in template Django

I am trying to display the get_context_data on the template. I have a method on the model class that I need to call from ProfileView which has two different models. For the Profile View I have Profile Model and for the shippingaddress view I have ShippingAddress Model. And these models are from two different app. I tried the function below and it does not show any error, but when I tried to call it in the Template, It does not show the method.
Views.py
class ProfileView(LoginRequiredMixin, DetailView):
model = Profile
template_name = "account/profile.html"
success_url = "/"
def get_context_data(self, *args, **kwargs):
context = super(ProfileView, self).get_context_data(**kwargs)
context['shipping'] = ShippingAddress.objects.filter(user=self.request.user)
return context
Template code
{{object.get_full_address}}
Models.py
class ShippingAddress(models.Model):
user = models.ForeignKey(settings.AUTH_USER_MODEL,
on_delete=models.CASCADE)
phone_number = PhoneNumberField(null=True, blank=True)
street_address = models.CharField(max_length=300)
province = models.CharField(max_length=300)
city = models.CharField(max_length=50)
country = models.CharField(max_length=50)
zip_code = models.CharField(max_length=10)
def __str__(self):
return str(self.user)
def get_phone_number(self):
return self.phone_number
#property
def get_full_address(self):
return f"{self.street_address}, {self.province}, {self.city}, {self.country}, {self.zip_code}"
object is the context variable that DetailView will add to the context. For your view this would be an instance of Profile. You pass a queryset into the context with the name shipping so you can loop over that:
{% for shipping_address in shipping %}
{{ shipping_address.get_full_address }}
{% endfor %}
Note: You need to loop because one user has multiple Shipping Addresses according to your models.
Note: Also you didn't need to override get_context_data you could simply have written:
{% for shipping_address in request.user.shippingaddress_set %}
{{ shipping_address.get_full_address }}
{% endfor %}
Where shippingaddress_set is the related model name with _set
appended. You can change that by setting related_name on your
foreign key.

error with slug in django - template it show all posts data in each post

Error with a slug in Django - template it shows all posts data in each post
when I create a new post and write my data it shows all data from other posts why is that?
and how I can fix it?
also how I can add an auto-generation slug?
models.py :
from django.urls import reverse
from django.utils.text import slugify
class Android(models.Model):
title = models.CharField(max_length=50,default="",help_text="this is title for slug not post!")
name = models.CharField(max_length=50,default="")
app_contect = models.CharField(max_length=240,default="")
app_image = models.ImageField(upload_to='images/',null=True, blank=True)
post_date = models.DateTimeField(auto_now_add=True, null=True, blank=True)
post_tag = models.CharField(max_length=50,default="",choices = BLOG_SECTION_CHOICES)
slug = models.SlugField(null=True,uniqe=True) # new
def get_absolute_url(self):
return reverse('android_posts', kwargs={'slug': self.slug}) # new
def get_image(self):
if self.app_image and hasattr(self.app_image, 'url'):
return self.app_image.url
else:
return '/path/to/default/image'
def __str__(self):
return self.name
class Meta:
ordering = ('-post_date',)
views.py :
def android_posts(request,slug):
android_posts = Android.objects.all()
context = {'android_posts':android_posts}
return render(request,'android/android_problems_fix.html', { 'android_posts': android_posts })
html page :
{% for android in android_posts %}
<h1 id="font_control_for_header_in_all_pages">{{android.name}}</h1>
<hr>
<p id="font_control_for_all_pages">{{android.app_contect}}</p>
{% endfor %}
url :
path('Android/<slug:slug>', views.android_posts, name='android_posts'),
To autogenerate your slug (and only do it on initial save, so that it remains consistent), add the generation to your model save method:
def save(self, *args, **kwargs):
super(<Model>, self).save(*args, **kwargs)
if not self.pk:
self.slug = <slugify code here>
As for your view/Template, you are specifically selecting all posts using:
android_posts = Android.objects.all()
Passing them to the template, then looping over them with the for loop to display them all.
Instead of this, select only a single object with:
android_post = Android.object.get(pk=<pk value>)
Edit after you added your urls.py:
You can get the unique object for a slug with:
android_post = get_object_or_404(Android, slug=slug)
The use of get_object_or_404 will also handle the case where that record doesn't exist.
You haven't posted your urls.py, so not sure how you're linking to this view, but if it includes the slug in the url, you will be able to get this in the view. My guess is you're not accessing via slug in the url, but via the id field.
Personally, when I slugify some text, I always include the id - it is a better way of ensuring uniqueness. By specifying unique=True on your non-pk slug field, you are likely restricting the titles people can use (2 people couldn't use the same title then!)
To give you an example, this is how I am doing it on one of my models:
def save(self, *args, **kwargs):
if not self.id or not self.slug:
super(Android, self).save(*args, **kwargs)
self.slug = slugify(f"{self.title} {str(self.id)}")
super(Android, self).save(*args, **kwargs)
This slug will always be unique because it includes id - and 2 people could have a record with the same title value without the system objecting.

Problem in displaying related objects in template for detail view

I want to render every {{ episode.object }} in single video.html page where it works fine for {{ video.object }}. But it isn't showing anything for episode object.. The final template video.html that I want to render episode objects can be seen here https://ibb.co/K9NMXtS
I tried
{% for episode in episodes %}
{{ episode.title }}
{% endfor %}
But that didn't worked. Here is the other configurations:-
#models.py
class Video(models.Model):
title = models.CharField(max_length=100, unique=True)
slug = models.SlugField(max_length=200, unique=True)
year = models.CharField(max_length=4)
category = models.CharField(max_length=3)
trailer = models.URLField(default='')
def __str__(self):
return self.title
def get_absolute_url(self):
from django.urls import reverse
return reverse("video.html", kwargs={"slug": str(self.slug)})
class Episode(models.Model):
video = models.ForeignKey(Video, related_name='episodes', on_delete=models.CASCADE)
title = models.CharField(max_length=512)
air_date = models.DateField()
videolink = models.URLField(default='')
def __str__(self):
return self.title
# urls.py
urlpatterns = [
path('video/<slug>/', views.VideoDetail.as_view(), name='videos'),
]
# view.py
class VideoDetail(DetailView):
model = Video
template_name = 'video/video.html'
def get_context_data(self, *args, **kwargs):
context = super().get_context_data(*args,**kwargs)
context['Episodes'] = Episode.objects.all()
return context
Python and Django templates are case sensitive. You use Episodes in the view, which doesn't match episodes in the template. Change one of them so that it matches (episodes is recommended for model instances in Python/Django).
Next, you are using Episode.objects.all() at the moment, which will display all episodes. If you only want the episodes for that video, then filter the queryset:
context['episodes'] = Episode.objects.filter(video=self.object)
Or you can get the same result by following the foreign key backwards:
context['episodes'] = self.object.episodes.all()
add VideoDetail, self in super tag
and while calling in template {{ episode.video.slug }}
and slug enough in url
path('video/<slug>/', views.VideoDetail.as_view(), name='videos'),
class VideoDetail(DetailView):
model = Episode
template_name = 'video/video.html'
def get_context_data(self, *args, **kwargs):
context = super(VideoDetail, self).get_context_data(*args,**kwargs)
context['Episodes'] = Episode.objects.all()
return context

How to delete / edit posts as staff or superuser?

I am fairly new to Django and right now not very familiar with the concept of Django. I created a user board (from simpleisbetterthancomplex.com) and would like to implement a Moderation with a "is_staff" user.
For now, only the user which created a post / comment is able to edit the post. As a is_staff user, I also want be able to edit ALL posts / comments.
This is the edit button:
{{ post.message }}
{% if post.created_by == user %}
<div class="mt-3">
<a href="{% url 'edit_post' post.topic.board.pk post.topic.pk post.pk %}"
class="btn btn-primary btn-sm"
role="button">Edit</a>
</div>
{% endif %}
I thought I could so something like:
{{ post.message }}
{% if user.is_staff %}
<div class="mt-3">
<a href="{% url 'edit_post' post.topic.board.pk post.topic.pk post.pk %}"
class="btn btn-primary btn-sm"
role="button">Edit</a>
</div>
{% endif %}
Although I reach the correct hyperlink to edit the post, I receive a "Page not found" error.
What could be an approach to implement some Moderation to my forum?
It's hard to tell what's wrong with your code. It seems both code samples should display the same button.
Please note that in this situation you should likely use a single button and tag, by changing the if to a slightly more complicated {% if user.is_staff or post.created_by == user %}. This should have the added effect of eliminating all possible discrepancies between the two buttons.
If you just want the ability to edit/delete posts, then the simplest way would likely be to use the built-in django admin panel. If you used django startproject, then your app already has one! Try going to localhost:8000/admin (by default) to check it out.
https://docs.djangoproject.com/pl/2.1/ref/contrib/admin/
EDIT: I think I can see the problem. You filter your queryset in PostUpdateView by (created_by=self.request.user). This filter works differently when dealing with a different user, such as the moderator. Try changing the view to reflect this.
The issue is the URL returned by edit_post. This view only allows access to owners of the post, so no one else can access this view.
You would need to add to the view to allow users with is_staff = True to also access this view.
The problem is with the queryset definition filtering out models not created by the user. But you want to keep this, so that other users don't have access.
You could remove the get_queryset call and adjust the dispatch method to only allow the owner or staff members to view. But I suggest keeping this intact and add a new moderator update view and use django braces to sort out the permissions. Something like;
from braces.views import StaffuserRequiredMixin
class ModeratorUpdateView(LoginRequiredMixin,
StaffuserRequiredMixin,
UpdateView):
## Update Code here ##
The view of my board:
class BoardListView(ListView):
model = Board
context_object_name = 'boards'
template_name = 'home.html'
class TopicListView(ListView):
model = Topic
context_object_name = 'topics'
template_name = 'topics.html'
paginate_by = 5
def get_context_data(self, **kwargs):
kwargs['board'] = self.board
return super().get_context_data(**kwargs)
def get_queryset(self):
self.board = get_object_or_404(Board, pk=self.kwargs.get('pk'))
queryset = self.board.topics.order_by('-last_updated').annotate(replies=Count('posts') - 1)
return queryset
#login_required
def new_topic(request, pk):
board = get_object_or_404(Board, pk=pk)
if request.method == 'POST':
form = NewTopicForm(request.POST)
if form.is_valid():
topic = form.save(commit=False)
topic.board = board
topic.starter = request.user # <- here
topic.save()
Post.objects.create(
message=form.cleaned_data.get('message'),
topic=topic,
created_by=request.user # <- and here
)
return redirect('topic_posts', pk=pk, topic_pk=topic.pk) # TODO: redirect to the created topic page
else:
form = NewTopicForm()
return render(request, 'new_topic.html', {'board': board, 'form': form})
def topic_posts(request, pk, topic_pk):
topic = get_object_or_404(Topic, board__pk=pk, pk=topic_pk)
topic.views += 1
topic.save()
return render(request, 'topic_posts.html', {'topic': topic})
#login_required
def reply_topic(request, pk, topic_pk):
topic = get_object_or_404(Topic, board__pk=pk, pk=topic_pk)
if request.method == 'POST':
form = PostForm(request.POST)
if form.is_valid():
post = form.save(commit=False)
post.topic = topic
post.created_by = request.user
post.save()
topic.last_updated = timezone.now()
topic.save()
topic_url = reverse('topic_posts', kwargs={'pk': pk, 'topic_pk': topic_pk})
topic_post_url = '{url}?page={page}#{id}'.format(
url=topic_url,
id=post.pk,
page=topic.get_page_count()
)
return redirect(topic_post_url)
else:
form = PostForm()
return render(request, 'reply_topic.html', {'topic': topic, 'form': form})
#method_decorator(login_required, name='dispatch')
class PostUpdateView(UpdateView):
model = Post
fields = ('message', )
template_name = 'edit_post.html'
pk_url_kwarg = 'post_pk'
context_object_name = 'post'
def get_queryset(self):
queryset = super().get_queryset()
return queryset.filter(created_by=self.request.user)
def form_valid(self, form):
post = form.save(commit=False)
post.updated_by = self.request.user
post.updated_at = timezone.now()
post.save()
return redirect('topic_posts', pk=post.topic.board.pk, topic_pk=post.topic.pk)
class PostListView(ListView):
model = Post
context_object_name = 'posts'
template_name = 'topic_posts.html'
paginate_by = 20
def get_context_data(self, **kwargs):
session_key = 'viewed_topic_{}'.format(self.topic.pk)
if not self.request.session.get(session_key, False):
self.topic.views += 1
self.topic.save()
self.request.session[session_key] = True
kwargs['topic'] = self.topic
return super().get_context_data(**kwargs)
def get_queryset(self):
self.topic = get_object_or_404(Topic, board__pk=self.kwargs.get('pk'), pk=self.kwargs.get('topic_pk'))
queryset = self.topic.posts.order_by('created_at')
return queryset
And the models.py:
from django.db import models
from django.contrib.auth.models import User
from django.utils.text import Truncator
import math
class Board(models.Model):
name = models.CharField(max_length=30, unique=True)
description = models.CharField(max_length=100)
def __str__(self):
return self.name
def get_posts_count(self):
return Post.objects.filter(topic__board=self).count()
def get_last_post(self):
return Post.objects.filter(topic__board=self).order_by('-created_at').first()
class Topic(models.Model):
subject = models.CharField(max_length=255)
last_updated = models.DateTimeField(auto_now_add=True)
board = models.ForeignKey(Board, on_delete=models.CASCADE, related_name='topics')
starter = models.ForeignKey(User, on_delete=models.CASCADE, related_name='topics')
views = models.PositiveIntegerField(default=0) # <- here
def __str__(self):
return self.subject
def get_page_count(self):
count = self.posts.count()
pages = count / 20
return math.ceil(pages)
def has_many_pages(self, count=None):
if count is None:
count = self.get_page_count()
return count > 6
def get_page_range(self):
count = self.get_page_count()
if self.has_many_pages(count):
return range(1, 5)
return range(1, count + 1)
def get_last_ten_posts(self):
return self.posts.order_by('-created_at')[:10]
class Post(models.Model):
message = models.TextField(max_length=4000)
topic = models.ForeignKey(Topic, on_delete=models.CASCADE, related_name='posts')
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(null=True)
created_by = models.ForeignKey(User, on_delete=models.CASCADE, related_name='posts')
updated_by = models.ForeignKey(User, on_delete=models.CASCADE, null=True, related_name='+')
def __str__(self):
truncated_message = Truncator(self.message)
return truncated_message.chars(30)

Using Multiple URL Parameters to get_object in Class-Based-View

Alright, I'm fairly new to this, I've been working on my Project for a couple months now and I'd like to create URLs that accept multiple parameters to call a View. A sample URL would look like this:
http://www.sample.com/builders//m//
I've got this implemented successfully, by overriding get_object in my DetailView, but I'm wondering if there is a better/easier method for accomplishing this or if this is considered a bad practice. Any guidance would be appreciated.
urls.py
urlpatterns = [
# url(r'^$', builder_list, name='list'),
# url(r'^create/$', builder_create, name='create'),
# url(r'^(?P<slug>[\w-]+)/$', builder_detail, name='detail'),
# url(r'^(?P<slug>[\w-]+)/edit/$', builder_update, name='update'),
# url(r'^(?P<slug>[\w-]+)/delete/$', builder_delete, name='delete'),
# url(r'^$', builder_list, name='sub_list'),
# url(r'^m/create/$', sub_create, name='sub_create'),
url(r'^(?P<builder>[\w-]+)/m/(?P<market>[\w-]+)/$', sub_detail, name='sub_detail'),
# url(r'^m/(?P<slug>[\w-]+)/edit/$', sub_update, name='sub_update'),
# url(r'^m/(?P<slug>[\w-]+)/delete/$', sub_delete, name='sub_delete'),
]
views.py
class BuilderSubDetailView(DetailView):
model = BuilderSub
template_name = "builders/sub_detail.html"
def get_context_data(self, **kwargs):
context = super(BuilderSubDetailView, self).get_context_data(**kwargs)
context['now'] = timezone.now()
print(context)
return context
def get_object(self, queryset=None):
if queryset is None:
queryset = self.get_queryset()
# Next, try looking up by primary key.
builder = self.kwargs['builder']
builder_id = Builder.objects.filter(slug=builder).first().pk
market = self.kwargs['market']
market_id = Market.objects.filter(slug=market).first().pk
if builder is not None and market is not None:
queryset = BuilderSub.objects.filter(parent=builder_id).filter(market=market_id)
# If none of those are defined, it's an error.
if builder is None or market is None:
raise AttributeError("Generic detail view %s must be called with "
"Builder and Market"
% self.__class__.__name__)
try:
# Get the single item from the filtered queryset
obj = queryset.get()
except queryset.model.DoesNotExist:
raise Http404("No %(verbose_name)s found matching the query") % \
{'verbose_name': queryset.model._meta.verbose_name}
return obj
And models.py for reference -- also is there any problem with my get_absolute_url function?
class Builder(models.Model):
added_by = models.ForeignKey(settings.AUTH_USER_MODEL, default=1)
company_name = models.CharField(max_length=80, help_text="Full Company Name", unique=True)
short_name = models.CharField(help_text="Short Company Name", max_length=30)
slug = models.SlugField(unique=True)
website = models.CharField(max_length=80, help_text="Format: www.[website].com")
logo = models.ImageField(blank=True, null=True)
timestamp = models.DateTimeField(auto_now_add=True)
info = RedactorField(verbose_name=u'Company Info')
def show_website_url(self):
return format_html("<a href='{url}'>{url}</a>", url=self.website)
def __str__(self):
return self.short_name
class BuilderSub(models.Model):
parent = models.ForeignKey(Builder)
market = models.ForeignKey(Market, null=True, blank=True)
details = RedactorField(verbose_name=u'Details', blank=True, null=True)
main_contact = models.ForeignKey(Person, blank=True, null=True)
def __str__(self):
return "{}: {} - {}".format(self.pk, self.market.name, self.parent.short_name)
def get_absolute_url(self):
return reverse('builders:sub_detail', kwargs={'market': self.market.slug, 'builder': self.parent.slug})
def pre_save_builder_reciever(sender, instance, *args, **kwargs):
instance.slug = slugify(instance.short_name)
pre_save.connect(pre_save_builder_reciever, sender=Builder)
I'm not 100% sure I'm my BuilderSub Model is the appropriate way to handle the Relationship between the overall Builder (company) and the Markets they serve so any guidance there would be appreciated as well.
Yes there is indeed a more ethical way to do this. DetailView is meant to deal with only one object. ListView however gets the job done!
I have replaced builder and market with city and category.
I am also a beginner. Hope I have answered your question :)
views.py
class EntryListView(generic.ListView):
template_name = 'myapp/category.html'
context_object_name = 'entry'
def get_queryset(self):
city_id = self.kwargs['city']
category_id = self.kwargs['category']
entry = Entry.objects.all().filter(city=city_id).filter(category=category_id)
return entry
urls.py
url(r'^(?P<city>[0-9]+)/(?P<category>[0-9]+)/$', views.EntryListView.as_view(), name='entry'),
category.html
{% extends 'myapp/base.html' %}
{% block body %}
<table>
{% for new in entry %}
<tr>
<td>
<img src = "{{new.image_url}}">
<br>
<b>Name :</b> {{new.name}}<br>
{% if new.phone %}
<B>Phone No. :</B> {{new.phone}}<br>
{% endif %}
<b>Address :</b> {{new.address}}<br>
</td>
</tr>
{% endfor %}
{% endblock %}
models.py
class Entry(models.Model):
city = models.ForeignKey(City, on_delete=models.CASCADE)
category = models.ForeignKey(Category, on_delete=models.CASCADE)
name = models.CharField(max_length=250)
phone = models.IntegerField(null=True)
address = models.CharField(max_length=250)
image_url = models.CharField(max_length=500)
def __str__(self):
return self.name