Slug Url Regex in Django - regex

After reading a lot about proper use of a slug to create a detail view from a list of objects. However, I am still having problems getting it to work for me. I am displaying a list of objects in my template like:
{% for thing in thing_list %}
<div class='thing-detail'><a href='{% url detail %}'><img src='theimage.png' />
{% endfor %}
But am getting a NoReverseMatch error on detail.
I figure that there is either something wrong with my regex, or there is just a better way of doing this that I am missing.
Regex:
url(r'^thing/(?P<slug>[\w-]+)/$', 'views.detail', name='detail'),
View:
def detail(request, slug):
thing = get_object_or_404(Thing, slug=slug)
return render(request, 'detail.html', {'thing': thing})
Model:
class Thing(models.Model):
user = models.ForeignKey(User)
created_on = models.DateTimeField(auto_now_add=True)
slug = models.SlugField()
def save(self, **kwargs):
slug = '%s' % (self.user)
unique_slugify(self, slug) ## from http://djangosnippets.org/snippets/1321/
super(Thing, self).save()
Thank you for helping!

You're not passing any arguments to build the detail URL. You probably want to do this:
{% url "detail" thing.slug %}
Which will create a detail URL with the given slug filled in.

Related

How I can get the pk in the home view www.mywebsite.com

I tried to get the pk from this function def home_view(request, pk): but because this in home view www.site.com it return error home_view() missing 1 required positional argument: 'pk' so it is didn't worked with me
My qustion is how I can pass pk to the template that in website domain without any extra urls like this
www.site.com
My view
def home_screen_view(request, pk, *args, **kwargs):
post= Post.objects.filter(pk=pk) #here I want to get the pk
comment = PostCommentIDF.objects.filter(post=post)
return render(request, 'personal/home.html', {'comment': comment)
My Urls
path('', home_screen_view, name='home'),
Edit
Firstly I return my posts using list [], I need the pk of the post because the comment model is separate from the post model so I use the pk to use in this function
posts = Post.objects.filter(pk=pk)
comment = PostCommentIDF.objects.filter(post=post)
post = []
for u in users:
p = Account.objects.get(username=u)
posts = p.post_set.all()
p_posts = p.video_set.all()
post .append(p_posts)
my_posts = request.user.post_set.all()
post.append(my_posts)
if len(post):
post = sorted(chain(*post), reverse=True, key=lambda post: post.created_date)
My comment MPTT model
class PostCommentIDF(MPTTModel):
post = models.ForeignKey(Post, on_delete=models.CASCADE, related_name='pos_com')
parent = TreeForeignKey('self', on_delete=models.CASCADE, null=True, blank=True, related_name='post_children')
author = models.ForeignKey(Account, on_delete=models.CASCADE)
content = models.TextField()
created_date = models.DateTimeField(auto_now_add=True)
My Post Model
class Post(models.Model):
author = models.ForeignKey(Account, on_delete=models.CASCADE)
article = models.TextField(null=True, blank=True)
photo_article = models.ImageField(max_length=255, upload_to=get_poster_filepath)
created_date = models.DateTimeField(auto_now_add=True)
My template that related to render the comments
{% recursetree video.total_comts %}
<div id="{{ node.id }}" class="my-2 p-2" style="border: 0px solid grey">
<div class="d-flex justify-content-between">
{{ node.publish|naturaltime }}
<div class="node-content mt-3">{{ node.content }}</div>
</div>
{% endrecursetree %}
Solution One
As you do not pass the pk information from url, you can't use pk in the view. So change the view to:
def home_screen_view(request):
#rest of the code
Instead of pk, if you want to see the logged in user's posts, you can use:
def home_screen_view(request):
posts = Post.objects.filter(account__user=request.user) # assuming there is a FK relation in Account model.
Solution Two
Also, you can't find pk arbitarily in the home view at / url, unless you pass it like /12/, if you pass that value then you need to update url code:
path('<int:pk>/', home_screen_view, name='home'),
Solution Three
You can use url querystring to get the primary key:
def home_screen_view(request):
pk = request.GET.get('pk', None)
posts = Posts.objects.all()
if pk:
posts = posts.filter(pk=pk)
Then use the browser url should look like https://example.com/?pk=1.
Update
If you want to get post specific comments, then you can use following solution:
# view
def home_screen_view(request):
return render(request, 'personal/home.html', {'posts': Post.objects.filter(author__user=request.user)})
# template
{% for post in posts %}
{% for comment in post.comment_set.all %} // if you use related_name in PostCommentIDF then you need to use post.<related_name>.all
{{ comment }}
{% endfor %}
{% endfor %}

How do I get an image from class Account, and use it in PostListView?

I got the images working on my template, but the image being displayed is the current logged in user's image.
How do I filter it so that Profile.objects.get(user=[the owner of the post's user])?
class PostListView(ListView):
model = Post
def get_context_data(self, **kwargs):
context = super(PostListView, self).get_context_data(**kwargs)
context['user_post'] = Post.objects.filter(user_id=self.request.user.id)
# context['user_profile'] = Profile.objects.get(user=self.request.user.id)
context['user_profile'] = Profile.objects.get(user=1)
return context
The error code says: 'PostListView' object has no attribute 'user'. I don't understand this error code because from my understand PostListView's model is set to Post which has the following models:
class Post(models.Model):
user = models.ForeignKey(to=settings.AUTH_USER_MODEL, on_delete=models.CASCADE)
title = models.CharField(max_length=200)
text = models.TextField()
...
So in other words every post made to the blog has a user.
def get_context_data(self, **kwargs):
context = super(PostListView, self).get_context_data(**kwargs)
context['user_post'] = Post.objects.filter(user_id=self.request.user.id)
# context['user_profile'] = Profile.objects.get(user=self.request.user.id)
context['user_profile'] = Profile.objects.get(user=1)
return context
What I believe self is doing here is represents the current object in PostListView right?
Can you please ELI5? I am having some trouble understanding the documentation since the language used in there is difficult for me.
Try something like this,
class PostListView(ListView):
template_name = 'add_template_name.html'
model = Post
context_object_name = 'all_posts'
def get_queryset(self):
return Post.objects.all()
Now in your template add this,
{% for post in all_posts %}
<img src="{{ post.user.profile_picture }}" alt="image">
<p> {{ post.title }} </p>
{% endfor %}
Whenever you want to use foreign key element, in this case user, you can choose post.user. Assuming your user model has profile_picture and username, you can display them as post.user.profile_picture and post.user.username respectively. For more info, you can go through Django docs.

django form drop-down choices are displayed "objects" instead of string representation of the model __str__

In my django form I am using a method to filter the drop down options to the ones that are related to the logged-in user. After the implementation the displayed values changed to objects rather than the __str__ value. I am posting the simplified codes and a snapshot that shows this. I have followed everything needed, but I cannot figure out why this is happening:
models.py
class Business(models.Model):
client=models.ForeignKey('Client',on_delete=models.CASCADE, limit_choices_to={'is_active':True},)
name=models.CharField(max_length=30,blank=False, unique=True,)
def __str__(self):
return self.name
class MMRequestAttributes(models.Model):
client=models.ForeignKey('Client',on_delete=models.CASCADE, limit_choices_to={'is_active':True},)
business=models.ForeignKey('Business', on_delete=models.CASCADE,limit_choices_to={'is_active':True},)
class Ticket(MMRequestAttributes):
no=models.CharField('Ticket Number',max_length=50,default=uuid.uuid4,null=False, blank=False, editable=False, unique=True)
subject=models.CharField('Subject',max_length=100,null=False, blank=False)
description=models.TextField('Description',max_length=500,null=True,blank=True)
created_at=models.DateTimeField('Created at',auto_now_add=True, editable=False)
updated_at=models.DateTimeField('Updated at',auto_now=True, editable=False)
created_by= models.ForeignKey(settings.AUTH_USER_MODEL)
status=StateField(editable=False)
def __str__(self):
return 'Ticket #' + str(self.pk)
views.py
def new_ticket(request):
form=NewTicket(request.user)
return render(request,'mmrapp/new_ticket.html',{'form':form})
admin.py
class UserExtend(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE, blank=False,null=False,)
client=models.ForeignKey('Client', on_delete=models.CASCADE,limit_choices_to={'is_active': True},)
forms.py
from django import forms
from .models import Ticket, Business
from .admin import UserExtend
from django.forms import ModelChoiceField
class NewTicket(forms.ModelForm):
def __init__(self,user, *args, **kwargs):
super(NewTicket, self).__init__(*args, **kwargs)
try:
client_id = UserExtend.objects.values_list('client_id', flat=True).get(user=user)
self.fields['business'].queryset=Business.objects.filter(client__id=client_id)
except UserExtend.DoesNotExist:
### there is not userextend corresponding to this user, do what you want
pass
class Meta:
model=Ticket
fields = ('subject','business')
new-ticket.html
{% extends 'mmrapp/__l_single_column.html' %}
{% load static %}
{% block main_col %}
<h1>New Ticket</h1>
<form method="POST" class="new-ticket">{% csrf_token %}
{{ form.as_p }}
<button type="submit" class="save btn btn-default">Submit</button>
</form>
{% endblock main_col %}
Finally I found the problem. Somehow django cannot load the original models' string representation of their objects (__str__) when showing them as choices in the drop downs. I had to explicitly define them again inside the from model. The attribute is called label_from_instance. The good thing is this way one can display them different from what they are originally defined in the models.
so the monkey patch would be:
self.fields['business'].label_from_instance = self.business_label
#staticmethod
def business_label(self):
return str(self.name)
In Python 2.7 we were using
def unicode(self):
return self.name
After the python upgrade from 2.7 to 3.6 it was showing all object references in the dropdown so I added
def str(self):
return self.name
I added this method to the model in question:
def __str__(self):
return self.whatever_prop_suits_your_needs

Model object has no attribute 'get'

using Django 1.11 I'm stuck with uuid referencing in my views.
I read through all similar looking questions here, but they either focus on forms or anything else.
Minimal example
It's an app called MegaTest. It's bound to /testuuid/
I have a working index view that generate links to /testuuid/<uuid>
If I click on a link on my index page, I get:
AttributeError at /testuuid/5a147a14-a9a9-4045-8e79-0ce2e8258a68/
'MegaTest' object has no attribute 'get'
models.py
class MegaTest(models.Model):
uuid = models.UUIDField(db_index=True, default=uuid.uuid4,
editable=False, unique=True)
name = models.CharField(max_length=128, blank=True, null=True)
views.py
class IndexView(generic.ListView):
template_name = 'testuuid/index.html'
context_object_name = 'test_list'
def get_queryset(self):
"""Return the last five published questions."""
return MegaTest.objects.all()
class MegaTestDetailView(generic.DetailView):
model = MegaTest
def get(self, request, uuid):
try:
megatest = MegaTest.objects.get(uuid=uuid)
return megatest
except MegaTest.DoesNotExist:
raise Http404
urls.py
app_name = 'testuuid'
urlpatterns = [
# ex: /polls/
url(r'^$', views.IndexView.as_view(), name='index'),
url(r'^(?P<uuid>[\w-]+)/$', views.MegaTestDetailView.as_view(), name='detail'),
]
testuuid/index.html
{% if test_list %}
<ul>
{% for test in test_list %}
<li>
{{ test.name }}</li>
{% endfor %}
</ul>
{% else %}
<p>No tests available.</p>
{% endif %}
I assume a damn simple mistake, but I really have no clue at this point.
The complete traceback can be found here http://dpaste.com/3JTB5NZ
You should override get_object() method instead of get().
class MegaTestDetailView(generic.DetailView):
model = MegaTest
def get_object(self):
try:
megatest = MegaTest.objects.get(uuid=self.kwargs['uuid'])
return megatest
except MegaTest.DoesNotExist:
raise Http404
You can also work with get_queryset() or even just specify pk field in url and don't write any method in the view class. Django will automatically query on your specified field in pk_url_kwarg

Creating Tag Index Django

Im using django-taggit to create a tagging system for a blog. How do you separate and filter objects so that only ones with selected tags are shown? Kind of like how on StackOverflow if you click on django
it will give you all the questions tagged django. I have tried the method described on this blog post, but I get an IndexError: tuple index out of range. This is the code I am using:
url(r'^tagged/(?P<tag>[\w-]+)/$', TagView.as_view(), name='tag_url'),
class TagView(ListView):
context_object_name = 'blog'
template_name = 'links/index.html'
def get_queryset(self):
return Blog.objects.filter(tags__name__in=[self.args[0]])
def get_context_data(self, **kwargs):
context = super(TagView, self).get_context_data(**kwargs)
context['requested_tag'] = self.args[0]
return context
<a href='{% url tag_url tag=tag %}'>{{ tag.name }}</a>
Am I missing something to get this method to work?
It seems like this is a pretty common programming necessity. Maybe you know a better method... Thanks for your ideas!
EDIT: TagView based on #catherine's suggestions:
class TagView(ListView):
model = Blog
context_object_name = 'blog_list'
template_name = 'tag-list.html'
def get_queryset(self):
queryset = super(TagView, self).get_queryset()
return queryset.filter(tags__name__in=self.kwargs['tags'])
class Blog(models.Model):
name = models.CharField(max_length=50)
date = models.DateTimeField()
slug = models.SlugField()
article = models.TextField()
tags = TaggableManager()
def __unicode__(self):
return self.name
tag-list.html:
{% block content %}
stuff
{% for blog in blog_list %}
{{ blog.article }}
{{ blog.name }}
{% endfor %}
{% endblock %}
The blog_list does not exist in the template, and no blog objects are available. Rather, only 'stuff' is rendered to the template. Any ideas are appreciated! Thanks!
class TagView(ListView):
model = Blog
......
def get_queryset(self):
# Fetch the queryset from the parent get_queryset
queryset = super(TagView, self).get_queryset()
return queryset.filter(tags__name__in=self.kwargs['tag'])
This answer is based on "EDIT: TagView based on #catherine's suggestions:".
You have a typo, in get_queryset method:
return queryset.filter(tags__name__in=self.kwargs['tags'])
you use tag and not tags thus it should be:
return queryset.filter(tags__name__in=[self.kwargs['tag']])