How can I make Django slug and id work together? - django

I want my URL to contain both id and slug like StackOverflow, but the slug is not working properly. Instead of being like this:
www.example.com/games/155/far-cry-5
the URL is like this:
www.example.com/games/155/<bound%20method%20Game.slug%20of%20<Game:%20Far%20Cry%205>>
My models.py :
class Game(models.Model):
name = models.CharField(max_length=140)
def slug(self):
return slugify(self.name)
def get_absolute_url(self):
return reverse('core:gamedetail', kwargs={'pk': self.id, 'slug': self.slug})
My views.py :
class GameDetail(DetailView):
model = Game
template_name = 'core/game_detail.html'
context_object_name = 'game_detail'
My urls.py :
path('<int:pk>/<slug>', views.GameDetail.as_view(), name='gamedetail')

Call the slug() method to get the value.
return reverse('core:gamedetail', kwargs={'pk': self.id, 'slug': self.slug()})
Or define it as a propery of the class
#property
def slug(self):
...

Related

Django get_absolute_url() doesn't seem to work in comment section

I'm trying to get my users to the article page after comments, but something is missing.
class Comment(models.Model):
post = models.ForeignKey(Post, related_name="comments" ,on_delete=models.CASCADE)
name = models.CharField(max_length=30)
body = RichTextUploadingField(extra_plugins=
['youtube', 'codesnippet'], external_plugin_resources= [('youtube','/static/ckeditor/youtube/','plugin.js'), ('codesnippet','/static/ckeditor/codesnippet/','plugin.js')])
date_added = models.DateTimeField(auto_now_add=True)
def __str__(self):
return '%s - %s' % (self.post.title, self.name)
class Meta:
verbose_name = "comentario"
verbose_name_plural = "comentarios"
ordering = ['date_added']
def get_absolute_url(self):
return reverse('article-detail', kwargs={'pk': self.pk})
urls.py
path('article/<int:pk>/comment/', AddCommentView.as_view(), name='add_comment'),
path('article/<int:pk>', ArticleDetailView.as_view(), name="article-detail"),
path('article/edit/<int:pk>', UpdatePostView.as_view(), name='update_post'),
path('article/<int:pk>/remove', DeletePostView.as_view(), name='delete_post'),
For the update_post the get_absolute_url() works. Thanks in advance.
You would need to pass a parameter which belongs to the ArticleDetailView model. For example if the model for the ArticleDetailView is Post:
class ArticleDetailView(DetailView):
model = Post
The get_absolute_url should use a post.pk:
class Comment(models.Model):
....
....
def get_absolute_url(self):
return reverse('article-detail', kwargs={'pk': self.post.pk})
In your case it is not working as it is using the Comment pk with the Article(Post) view
Ok the thing is I was using this class for comment, had to delete the function of success_url and now works.
class AddCommentView(CreateView):
model = Comment
form_class = CommentForm
#form_class = PostForm
template_name = 'add_comment.html'
success_url = reverse_lazy('home')
def form_valid(self,form):
form.instance.post_id = self.kwargs['pk']
return super().form_valid(form)

How to reference a slug from a different model in get_success_url?

Setup
I have two models in my app. One is for a Journal and the other one is for entries to that journal. I wrote a CreateView class that will allow user to create a Journal entry for any Journal id currently located in. Ideally I want the class to "refresh" with the updated entry or in other words the get_success_url should lead the page we are currently located at.
views.py
class ToJournalEntriesList(LoginRequiredMixin, CreateView):
model = to_journal_entry
template_name = 'to_journals/to_journal_entries_list.html'
fields = ('body',)
def get_success_url(self):
return reverse('to-journals', kwargs={'slug':self.object.slug})
def form_valid(self, form):
current_journal = to_journal.objects.get(journal_user=self.request.user, slug=self.kwargs['slug'])
form.instance.journal_user = self.request.user
form.instance.journal_name = current_journal
return super(ToJournalEntriesList, self).form_valid(form)
def get_context_data(self, **kwargs):
context = super(ToJournalEntriesList, self).get_context_data(**kwargs)
context['to_journal_entries'] = to_journal_entry.objects.all()
return context
models.py
class to_journal(models.Model):
journal_name = models.CharField(max_length = 40)
slug = AutoSlugField(populate_from='journal_name')
date_created = models.DateTimeField(auto_now_add=True)
journal_user = models.ForeignKey(get_user_model(), on_delete=models.CASCADE,)
def __str__(self):
return str(self.journal_user) + " " + self.journal_name
def get_absolute_url(self):
return reverse('to-journals')
class to_journal_entry(models.Model):
body = models.TextField()
entry_date = models.DateTimeField(auto_now_add=True)
journal_name = models.ForeignKey(to_journal, on_delete=models.CASCADE,)
journal_user = models.ForeignKey(get_user_model(), on_delete=models.CASCADE,)
def __str__(self):
return str(self.journal_name) + " " + str(self.entry_date)
def get_absolute_url(self):
return reverse('to-journal-entries', args=(self.slug))
urls.py
urlpatterns = [
path('', CreateToJournal.as_view(), name='to-journals'),
path('<slug:slug>', ToJournalEntriesList.as_view(), name='to-journal-entries'),
]
Error
With the current setup that I have I get:
Which makes sense, because the to_journal_entry model does not have that that field.
Question
I am sure both my get_success_url() in views.py and get_absolute_url() in models.py are done incorrectly, but I could not find a good explanation of how those work. How should I set them up to achieve desired result? Thanks a ton in advance!
I appreciate everyone taking a look. Best, Rasul.
You can just follow the relationship:
def get_success_url(self):
return reverse('to-journals', kwargs={'slug': self.object.journal_name.slug})
Your self.object is a to_journal_entry. You probably want to use the to_journal, you can do that by obtaining the journal_name:
class ToJournalEntriesList(LoginRequiredMixin, CreateView):
model = to_journal_entry
template_name = 'to_journals/to_journal_entries_list.html'
fields = ('body',)
def get_success_url(self):
return reverse('to-journals', kwargs={ 'slug': self.object.journal_name.slug })
Note: usually the names of the models are written in PerlCase, so JournalEntry instead of to_journal_entry.

Redirection issue using PK

I Have a very strange phenomenon
In my app the user Create a project and is redirected to that project detail using its pk. On that project detail page he is asked to create a team and when the team is created he is redirected again to the project detail page but to the wrong pk
for example: I just created a project and I got redirected to .../project/24. I was asked to create a team, I created it but got redirected to ../project/17 any idea why and how to redirect my page to the right URL ?
model.py:
class Team(models.Model):
team_name = models.CharField(max_length=100, default = '')
team_hr_admin = models.ForeignKey(MyUser, blank=True, null=True)
def get_absolute_url(self):
return reverse('website:ProjectDetails', kwargs = {'pk' : self.pk})
def __str__(self):
return self.team_name
class TeamMember(models.Model):
user = models.ForeignKey(MyUser)
team = models.ForeignKey(Team)
def __str__(self):
return self.user.first_name
class Project(models.Model):
name = models.CharField(max_length=250)
team_id = models.ForeignKey(Team, blank=True, null=True)
project_hr_admin = models.ForeignKey(MyUser, blank=True, null=True)
def get_absolute_url(self):
return reverse('website:ProjectDetails', kwargs = {'pk' : self.pk})
def __str__(self):
return self.name
views.py
class TeamCreate(CreateView):
model = Team
fields = ['team_name']
template_name = 'team_form.html'
def form_valid(self, form):
valid = super(TeamCreate, self).form_valid(form)
form.instance.team_hr_admin = self.request.user
obj = form.save()
#SELECT * FROM project WHERE user = 'current_user' AND team_id = NULL
obj2 = Project.objects.get(project_hr_admin=self.request.user, team_id=None)
obj2.team_id = obj
obj2.save()
return valid
return super(TeamCreate, self).form_valid(form)
def get_success_url(self):
project = Project.objects.get(team_id=self.obj, project_hr_admin=self.request.user)
return project.get_absolute_url()
The problem here is your CreateView is refering to a TeamObject and not project.
You should override the get_success_url method:
def get_success_url(self):
project = Porject.objects.get(team_id=self.object, project_hr_admin=self.request.user)
return project.get_absolute_url()
The function called was the get_absolute_url of your Team model. So you're calling the project detail view but with the team pk => you get a random project assuming there's a project with a pk which has the same value as your project or, the pk you're sending doesn't exist and you'll have a 404 error (pk doesn't exist).
def get_absolute_url(self):
return reverse('website:ProjectDetails', kwargs = {'pk' : self.pk})
That's the one in your Team model, but you call ProjectDetails. So here, self.pk is Teaminstance.pk.
What I do in the code i gave you is to call the get_absolute_url of the project instance.
But as told in the other answer, you should remove or change your get_absolute_url of your team model.
class Team(models.Model):
# ...
def get_absolute_url(self):
return reverse('website:ProjectDetails', kwargs = {'pk' : self.pk})
^^^^^^^
Here, the wrong pk will be delived. Thx to #Bestasttung for clarification

Django Slug Not Working In get_absolute_url

Whats wrong with this code? get_absolute_url is blank when rendered in my template, which means It's failing somewhere.
I suspect it's the slug as this is the first time I have tried to use it within Django:
Thanks
Model:
class Entry(models.Model):
title = models.CharField(max_length=200)
slug = models.SlugField(max_length=255, unique=True)
def get_absolute_url(self):
return reverse("EntryDetail", kwargs={"slug": self.slug})
URL:
url(r'^entry/(?P<slug>[^\.]+).html',
blog_views.EntryDetail.as_view(),
name='blog_entry'),
View:
class EntryDetail(DetailView):
context_object_name = 'entry'
template_name = "blog.entry.html"
slug_field = 'slug'
def get_object(self):
return get_object_or_404(Entry, url=self.slug_field)
Based on what #Peter DeGlopper stated in comments: It looks like your trying to get the absolute URL using a Class Based View. Don't use reverse here, instead you should always give your URLs a name, and then refer to that.
For example this is how it should look:
model
#models.permalink
def get_absolute_url(self):
return 'blog_entry', (), {'slug': self.slug}
urls
url(r'^entry/(?P<slug>[^\.]+).html',
blog_views.EntryDetail.as_view(),
name='blog_entry'),

Django and models / views, AttributeError

I am stuck with my miniportfolio in django, not sure what the error is telling me.
models.py
class Page(models.Model):
title = models.CharField(max_length=30)
slug = models.SlugField()
content = models.TextField()
def get_absolute_url(self):
return "/%s" % self.slug
def __unicode__(self):
return self.title
def __repr__(self):
return self.name
views.py
def page_view(request, **kwargs):
slug = kwargs.get('slug')
page = get_object_or_404(Page, slug=slug)
content = page.content
title = page.title
return render_to_response("base.html", {"content":content, "title":title})
urls.py
url(r'^(?P<page>)/$', page_view),
I get some really weird urls when I create a new page from the admin interface.
127.0.0.1:3020/admin/r/7/3/ <-- I do not know why they are like this. Tips?
When I visit the pages I've created I get a 404.
change:
def get_absolute_url(self):
return ('page', (), {
'slug': self.slug,
'id': self.id,
})
to something like:
def get_absolute_url(self):
return "/APP_URL_PATH/%s/" % self.slug # or use id, there's no need for two unique identifiers
and
def my_view(request, slug, id):
page = get_object_or_404(pk=id)
to something like:
def my_view(request, **kwargs):
slug = kwargs.get('slug')
page = get_object_or_404(Page, slug=slug)
Your indention is probably bad because of copy pasting, you might want to edit your post. Have a look at django's documentation for an idea on url capture groups
def page_view(request, **kwargs):
slug = kwargs.get('page') # hello page! I found you
...
because you use page for variable not slug in urls.py
url(r'^(?P<page>)/$', page_view),
Or you can write view like
def page_view(request, page=None):
page_from_db = get_object_or_404(Page, slug=page)
return render(request, "base.html", {"page": page_from_db})
in tpl
{{ page.content }}
And if you use 1.4 use from django.shortcuts import render
render automatically use context