I'm hoping this is just an issue of my poor regex understanding.
I'm attempting to use the exact code on Django 1.9's generic views to build a blog and personal site, and, down to the testing, here's where I run into trouble:
def test_post_page(self):
post = PostModelFactory()
first_post = Post.objects.all()[0]
post_url = first_post.get_absolute_url()
print(post_url)
response = self.client.get(post_url, follow=True)
self.assertEqual(response.status_code, 200)
So, through that print statement, I determined models.Post.get_absolute_url() was returning my homepage URL. Here's models.py:
class Post(models.Model):
title = models.CharField(max_length=200)
subtitle = models.CharField(max_length=200, default="")
pub_date = models.DateTimeField(auto_now_add=True)
text = models.TextField()
slug = models.SlugField(max_length=40,unique=True)
def get_absolute_url(self):
return "%s/" % (self.slug)
Should it come up, I copied down what the generic views documentation has, so my Detailview in /blog/urls.pyis as follows:
url(r'^(?P<slug>[-\w]+)/$', PostDetailView.as_view(), name='post-detail'),
Same of views.py:
class PostDetailView(DetailView):
model = Post
def get_context_data(self, **kwargs):
context = super(PostDetailView, self).get_context_data(**kwargs)
context['now'] = timezone.now()
return context
As far as I can tell, my get_absolute_url() function is simply not doing what I think it's doing, let alone what the regex in urls.py expects it to do.
Also: Is there anyone who can fully explain how slugfield works? I know it's a keyword generator to create a url, but I'm not sure how it works (or doesn't, as in this example).
Then, finally, in both a tutorial that I'm quasi-following alongside the documentation, and the documentation, itself, I'm not fully understanding where the variable names in templates are coming from (my understanding is that the request hit's the URL, which generates the data from views.py). The "ListView" object in the template shares the model name, "post" (or "article" in the documentation), where its pageview at the bottom is accessed simply through "page_obj", and the "DetailView" object is simply called "object". I also may be having a problem with paginating my ListView, ( which is identical to the documentation example, but with the extra line paginate_by = 2 right above get_context_data.
Thank you.
EDIT:
I've included PostModelFactory:
class PostModelFactory(DjangoModelFactory):
class Meta:
model = Post()
django_get_or_create = (
'title',
'subtitle',
'text',
'pub_date',
)
title = 'This is a test.'
subtitle = 'This is only a test.'
text = 'Madness? This is Sparta.'
pub_date = timezone.now()
def __init__(self):
self.save()
Edit: The issue turned out to be the lack of a slug in the PostModelFactory.
Ideally, you should use reverse in get_absolute_url, instead of hardcoding it.
from django.core.urlresolvers import reverse
class Post(models.Model):
...
def get_absolute_url(self):
return reverse('post-detail', args=[self.slug])
If you do hardcode the URL, it should contain a leading slash.
def get_absolute_url(self):
return "/%s/" % (self.slug)
If first_post.get_absolute_url is returning the homepage url with your current get_absolute_url, that suggests that the slug is an empty string.
Related
I am learning django by building a simple blogging app. While its all but done, I currently have individual posts having a url in the format https://my_site_dot_com/blog/entry/38/ where the number 38 corresponds to the primary key of said post.
What i want is it to have the format https://my_site_dot_com/blog/entry/this_is_custom_title/ where "this_is_custom_title" corresponds to the heading of the post. I have no idea how to accomplish this. Can anyone offer any assistance?
My model looks like:
class Entry(models.Model):
entry_title = models.CharField(max_length=50)
entry_text = models.TextField()
image = models.FileField(upload_to="media", blank=True)
entry_date = models.DateTimeField(auto_now_add=True)
entry_author = models.ForeignKey(User, on_delete=models.CASCADE)
class Meta:
verbose_name_plural = "blog"
def __str__(self):
return self.entry_title
I want the entry_title to the the custom url instead of the primary key.
My urls.py looks like this:
urlpatterns = [
path('', HomeView.as_view(), name="blog-home"),
path('entry/<int:pk>/', EntryView.as_view(), name="entry-detail"),
path('create_entry/', CreateEntryView.as_view(success_url='/'), name='create_entry'),
]
Edit:
The class handing the post looks like this:
class EntryView(DetailView):
model = Entry
template_name = 'blog/entry_detail.html'
data_set = random_info()
stuff_for_post = {
"info": data_set
}
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['rand_im'] = random_image()
context['tags'] = ['tag1','tag2','tag3']
return context
I'm an absolute noob in django and come from android/java. So please give an easy to understand explanation.
Thanks in advance
You may add a slug field to your Entry model and a get_absolute_url method. Don't forget to import reverse function from Django's url module.
from django.urls import reverse
class Entry(models.Model):
entry_title = models.CharField(max_length=50)
entry_text = models.TextField()
image = models.FileField(upload_to="media", blank=True)
entry_date = models.DateTimeField(auto_now_add=True)
entry_author = models.ForeignKey(User, on_delete=models.CASCADE)
slug = models.SlugField()
def get_absolute_url(self):
return reverse('entry_detail', kwargs={'slug': self.slug})
class Meta:
verbose_name_plural = "blog"
def __str__(self):
return self.entry_title
Then, within the urls.py module of your app, add the following url pattern to the urlpatterns list. Don't forget to load the corresponding view, I guess it may be EntryView in this case.
from django.urls import path
from .views import EntryView
urlpatterns = [
...
path('<slug:slug>', EntryView.as_view(), name='entry_detail'), # new
...
]
Then the slug should replace the primary key pattern in the url.
To go a bit further, you can use a method within your model that slugify your title for instance. (define the method within the model then call it from the save method of the model, by overriding the save method)
https://docs.djangoproject.com/en/3.0/ref/utils/#django.utils.text.slugify
Currently you are passing an integer through your url. All you need to do is modify this slightly to pass a string through the url. Here is a similar question that discusses how to accomplish this.
As for changes you need to make in your code, urls.py will need to be updated
path('entry/<str:title>/', EntryView.as_view(), name="entry-detail")
You haven't provided your blog post view, but it will then look something like this:
def post(request, title):
template = "template.html"
post = Posts.objects.filter(entry_title==title)
return render(request, template, {'post':post})
If you are using a Class Based View you should use a slug.
First add a new field entry_slug to your Entry model and override the save method in order to automatically generate the entry_slug field:
class Entry(models.Model):
entry_title = models.CharField(max_length=50)
entry_slug = models.CharField(max_length=50)
...
def save(self, *args, **kwargs):
self.entry_slug = slugify(self.entry_title )
super(Entry, self).save(*args, **kwargs)
You can do by replacing the pk with entry_slug:
path('entry/<slug:entry_slug>/', EntryView.as_view(), name="entry-detail")
this is the first time I had to ask something at StackOverflow, I'm both excited and scared, I don't know why.
I'm writing a Django app that just hosts web posts. The page is divided into three (3) categories (Index, lkmi and chiqung). Each post has a category (lkmi or chiqung).
On the INDEX page, you can see all the posts.
On the LKMI page, you can see only the lkmi posts.
On the CHIQUNG page, you can see only the chiqung posts.
All is controlled just by ONE VIEW called "index_page", which receives an argument called "cat" that is the URL from one of the categories (index, lkmi, chiqung). Based on that it dictates which posts to load.
* NOW THE PROBLEM *
I can't find why, but I'm only having trouble loading the lkmi section. The index page and the chiqung_page loads perfectly, but I'm having a
"Page Not Found (404)
Request Method: GET
Request URL: http://127.0.0.1:8000/lkmi/
Using the URLconf defined in blog.urls, Django tried these URL patterns, in this order:
admin/
<cat> [name='index_page']
post/<int:pk> [name='post_detallado']
^ckeditor/
The current path, lkmi/, didn't match any of these.
"
I'll leave here the models, views and urls.
Models
Category
class Category(models.Model):
name = models.CharField(max_length=50)
presentation = RichTextUploadingField(default='')
order = models.IntegerField(null=True)
url = models.CharField(max_length=50,default='',null=False,blank=True)
class Meta:
verbose_name = ("Categoria")
verbose_name_plural = ("Categorias")
def __str__(self):
return self.name
def get_postes(self):
print('Entre get_postes')
if self.url == '/':
return Post.objects.all().order_by('-published_date')
else:
return Post.objects.filter(category=self.pk).order_by('-published_date')
Post
class Post(models.Model):
title = models.CharField(('Titulo'),max_length=50)
category = models.ForeignKey("core.Category", verbose_name=("Categoria"), null=True, on_delete=models.CASCADE)
creation_date = models.DateTimeField(auto_now = True)
published_date = models.DateTimeField(auto_now=False, auto_now_add=False)
content = RichTextUploadingField()
author = models.CharField(("Autor/a"), max_length=50, default='El Autor')
class Meta:
verbose_name = ("Post")
verbose_name_plural = ("Postes")
def __str__(self):
return self.title
def get_absolute_url(self):
return reverse("_detail", kwargs={"pk": self.pk})
View That Handles The Categorization
def index_page(request, cat):
print('function index_page')
print('cat ='+str(cat))
category = get_object_or_404(Category,url=cat)
print('get_object_or_404(Category,url=cat). Executed')
print(str(category))
postes = category.get_postes()
print('category.get_postes(). Executed')
return render(request, 'index.html',{'postes': postes,
'category': category})
URL file from the app
urlpatterns = [
path('admin/',admin.site.urls),
path('<cat>', views.index_page, name='index_page'),
path('post/<int:pk>', views.detailed_post, name='detailed_post'),
]
Index Page
It loads
Chiqung Page
It loads
LKMI Page
It doesn't load...
If i use python manage.py shell and look for the category using Category.objects.get(url='lkmi') it returns the desired Category. Based on the comments of the view, it doesn't even enter the view function, because in the console it doesn't output "function index_page", even though it is the first line of the function.
Also, if i try the url "**127.0.0.1:8000/lkmi" it gives
Using the URLconf defined in blog.urls, Django tried these URL patterns, in this order:
1. admin/ -
2. <cat> name='index_page'] -
3. post/<int:pk> [name='post_detallado'].
But if i just enter "127.0.0.1:8000/whatever" the error is just
No Categoria matches the given query.
So in this case it entered the function and it gave the 404 error of not finding a matching Category. I still can't understand why it doens't execute the view when i try to enter to the lkmi url..
Thank you very much, I hope someone can help me with this so I cant continue going.
Your class is called "Category", but you call get_object_or_404() on "Categoria"
View should be:
def index_page(request, cat):
categoria = get_object_or_404(Category,url=cat)
postes = categoria.get_postes()
return render(request, 'index.html',{'postes': postes,
'categoria': categoria})
I'm trying to implement a DeleteView and prevent the model from being deleted if the user is not the owner. I'm sure it's a pretty trivial task but i can't manage to find a clear answer. So far I can see my view but currently everyone can delete the object.
Here is my code :
views.py :
#method_decorator(login_required, name='dispatch')
class RestaurantDeleteView(DeleteView):
model = Restaurant
template_name = 'restaurant/delete_form.html'
success_url = '/'
models.py :
class Restaurant(models.Model):
owner = models.ForeignKey(User, on_delete=models.CASCADE)
name = models.CharField(max_length=200)
category = models.CharField(max_length=200)
description = models.TextField()
capacity = models.IntegerField(default=0)
def get_absolute_url(self):
return reverse('detail', kwargs={'pk': self.pk})
Thanks in advance !
This might not be the best way, but it illustrates the point.
In your views.py, you can access self.get_object() to get the model instance in reference, and then check to see if instance.owner == self.request.user. Look at the source code and try to remain faithful as much possible to the original. All you need is a conditional check.
Something like this:
#method_decorator(login_required, name='dispatch')
class FooDeleteView(DeleteView):
model = Foo
success_url = reverse_lazy('index')
def get(self, request, *args, **kwargs):
self.object = self.get_object()
if self.object.owner != self.request.user:
return redirect(self.success_url)
return super().post(request, *args, **kwargs)
If you are curious and would like to know more, you can import pdb and place pdb.set_trace() on the first line of the method, and explore what self is and what methods is available to it. You can do this by callin dir(self) once you inside the method call. You'd be surprised with the amount of things you'd discover.
Really new to Django so bear with me :)
I am running into an issue to display the posts titles in the Django admin.
I have tried both in Python 3
class Post(models.Model):
title = models.TextField(max_length=100)
text = models.TextField(max_length=10000)
tags = models.TextField(max_length=300)
comments = models.TextField(max_length=400)
def __str__(self):
return self.title
and Python 2
class Post(models.Model):
title = models.TextField(max_length=100)
text = models.TextField(max_length=10000)
tags = models.TextField(max_length=300)
comments = models.TextField(max_length=400)
def __unicode__(self):
return self.title
but unfortunately in the Django admin I see
"Post object "in the list of posts
Thanks in advance for your help.
Maybe you can try this:
from django.utils.encoding import python_2_unicode_compatible
#python_2_unicode_compatible
class Post(models.Model):
title = models.CharField(max_length=255)
text = models.TextField(max_length=10000)
tags = models.TextField(max_length=300)
comments = models.TextField(max_length=400)
def __str__(self):
return self.title
For those who might come here after me, you'd have to add this method inside the model class.
def __str__(self):
return self.title
Make sure it's indented right or else it might not work.
Add your post models to the administration site. Edit the admin.py file of your app and make it look like this:
from django.contrib import admin
from .models import Post
admin.site.register(Post)
The admin has many hooks for customization check documentation
There is no way to have Django return, say, a MyPerson object whenever you query for Person objects. A queryset for Person objects will return those types of objects. The whole point of proxy objects is that code relying on the original Person will use those and your own code can use the extensions you included (that no other code is relying on anyway
class Person(models.Model):
name = models.CharField(max_length=128)
def __str__(self):
return self.name
Know more
For those who come after, make sure post is in lowercase.
And after which make migrations again.
make sure name='post' not 'Post'...
migrations.AlterModelOptions(
name='post',
I'm trying to get something in Django, already for hours but without success. However I'm trying to use friendly urls like 'my-post-title-1234', where the number at the end is the post's id and the rest before that is the post's title. I got the url by using the slug and the id and I can retrive both in the view. So I check if ID exists and if it exists I do the rest, and if it doesn't exist I do 404 DoesNotExist. Inside the model I generated slug field and slugified title.
Everything works so far except one thing: User is able to write bla-bla-bla-1234 and it will still show him same data (since id exist). What I would like is following:
If user type in 'bla-bla-bla-1234' I would like to redirect him to correct slug 'my-post-title-1234'.
This is how my url looks like:
url(r'^(?P<slug>[-\w\d]+)-(?P<post_id>\d+)/$', views.post, name='post')
This is my model:
class Post(models.Model):
post_title = models.CharField(max_length = 125)
text = models.TextField()
slug = models.SlugField(null = False, blank = True)
def __str__(self):
return self.post_title
def save(self, *args, **kwargs):
self.slug = slugify(self.post_title)
super(Post, self).save(*args, **kwargs)
This is from my view:
def post(request, slug, post_id):
try:
post = Post.objects.get(id = post_id)
except Post.DoesNotExist:
raise Http404("Post does not exist")
return HttpResponse(post_id)
So the question is: how do I redirect(change url) to the correct slug from 'bla-bla-bla-1234' to 'my-post-title-1234', if user type in slug incorrectly while id is still good.
Thanks a lot.
Seems like you just need to check if the slug is correct, and if not do a redirect.
from django.shortcuts import get_object_or_404, redirect
def post(request, slug, post_id):
obj = get_object_or_404(Post, pk=post_id)
if obj.slug != slug:
return redirect('post', slug=obj.slug, post_id=obj.pk)
Note that there's a built-in shortcut for your first four lines: get_object_or_404. Also, be aware that calling an object within the function the same name as the function itself can lead to confusion; avoid doing that (which is why I've use obj above.)
In Django redirect comes with parameters, you can simply pass the slug in redirect by putting a ",".
return redirect("post",slug=slug)