how to pass url to a model field lookup in django - django

Having some trouble finding how to pass a var captured in a url to my view.
say I have a model like this...
title = models.CharField()
and a title of...
title = 'this is a post'
then how would django capture something in the URL like this...
url/this_is_a_post/
and lookup the model field like this...
x = Post.objects.get(title='this is a post')
I have another URL that is capturing a name with a space in it, and when I use a URL with "_"'s in it, it looks it up correctly, but in this case, it is telling me there is no matching query. I have tried to look in the docs, but I couldn't find anything although I know it has to be there, somewhere.

Use two fileds in your models, say;
class Post(models.Model):
title = models.CharField(max_length=100)
slug = models.SlugField(max_length=100)
and while saving yor Post, you shoud save slug also;
In views;
from django.template.defaultfilters import slugify
post = Post.objects.create(title=title, slug=slugify(title))
this slug will be unique for each post.
In urls.py
url(r'^(?P<slug>[\w-]+)/$', 'myapp.views.post_detail', name='post_detail'),
In views.py
def post_detail(request, slug):
x = Post.objects.get(slug=slug)

in models.py:
title = models.SlugField(unique=True)
in urls.py:
urlpatterns = [
...
url(r'^(?P<title>\d+)/$', views.article_view, name = 'article_detail'),
...
]
in views.py:
def article_view(request, title):
...
article = get_object_or_404(Article, title=title)
...
in template.html:
<a href="{% url 'article_detail' title=article.title %}">

Related

How can use slug for all urls in django without anything before or after?

I want all djaango urls use slug field without any parameter before or after, by default
just one url can use this metod
Views.py
class ArticleDetail(DetailView):
def get_object(self):
slug = self.kwargs.get('slug')
article = get_object_or_404(Article.objects.published(), slug=slug)
ip_address = self.request.user.ip_address
if ip_address not in article.hits.all():
article.hits.add(ip_address)
return article
class CategoryList(ListView):
paginate_by = 5
template_name = 'blog/category_list.html'
def get_queryset(self):
global category
slug = self.kwargs.get('slug')
category = get_object_or_404(Category.objects.active(), slug=slug)
return category.articles.published()
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['category'] = category
return context
urls.py
urlpatterns = [
path('<slug:slug>', ArticleDetail.as_view(), name="detail"),
path('<slug:slug>', CategoryList.as_view(), name="category"),
]
This is my django blog codes,
I don't want write article or category & ... in urls, just slug
mysite .com/article-slug
...
mysite .com/category-slug
It will always trigger the Article view, regardless if there is an Article for that slug. You thus should make the URL patterns non-overlapping such that the other views can be triggered, for example with:
path('article/<slug:slug>/', Article.as_View(), name="articledetail"),
path('category/<slug:slug>/', Category.as_View(), name="category"),
path('product/<slug:slug>/', Product.as_View(), name="productdetail"),
If you want a path that accepts a single slug, you should define a view that looks if there is an Article with that slug, if that is not the case a Category and if that is not the case a Product you thus implement that logic in the view, not in the URL patterns.
#WillemVanOlsem is right, you will have to write a view like this:
from django.http import HttpResponseNotFound
def slug_router(request, slug):
if Category.objects.filter(slug=slug).exists():
return CategoryList.as_view()(request, slug=slug)
elif Article.objects.filter(slug=slug).exists():
return ArticleDetail.as_view()(request, slug=slug)
else:
return HttpResponseNotFound('404 Page not found')
And then
urlpatterns = [
path('<slug:slug>', slug_router, name="slug"),
]
... if I'm not mistaken. This should be the jist of it.
I didn't test this code, just typed it in here, so let me know if it doesn't work, I'll help to fix it.
Note that you'll have a preference if there are Articles with the same slug as some Categories.

why django model can't save my new object when i click create button in 'create.html'

I'm beginning to use Django, and I have some problems. I want to create new post such as blog. So I use views.py with model.py and forms.py.
but when I enter the create.html, I was writing what i want to post, and then click 'create' button. but it wasn't save in django object. I check in admin site, but there is no object. I think it means save object is failed. but I don't know where is the problem. plz help me T.T
in views.py
def create(request):
if request.method =="POST":
filled_form = ObForm(request.POST)
if filled_form.is_valid():
filled_form.save()
return redirect('index')
Ob_form = ObForm()
return render(request, 'create.html', {'Ob_form':Ob_form})
in create.html
<body>
<!-- form.py 모델 생성 -->
<form method="POST" action="">
{% csrf_token %}}
{{Ob_form.as_p}}
<input type="submit" value="확인" />
</form>
</body>
in models.py
from django.db import models
class Ob(models.Model):
title = models.CharField(max_length=50)
image = models.ImageField(null=True)
content = models.TextField(null=True)
update_at = models.DateTimeField(auto_now=True)
in forms.py
from django import forms
from .models import Ob
# 모델폼을 상속받아서 모델폼이 되었음
class ObForm(forms.ModelForm):
# 어떤 모델과 대응되는지 말해줌
class Meta:
model = Ob
fields = ( "title", "image", "content")
# 모델 폼 커스텀
# init - 내장함수 - (해당 클레스에 들어오는 여러가지 인자를 받을 수 있는 파라미터)
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.fields['title'].label = "제목"
self.fields['image'].label = "사진"
self.fields['content'].label = "자기소개서 내용"
self.fields['title'].widget.attrs.update({
'class': 'Ob_title',
'placeholder': '제목',
})
and urls.py
from django.urls import path
from .views import index, create, detail, delete, update
urlpatterns = [
path('', index, name="index"),
path('create/', create, name="create"),
path('detail/<int:Ob_id>', detail, name="detail"),
path('delete/<int:Ob_id>', delete, name="delete"),
path('update/<int:Ob_id>', update, name="update"),
]
A bit of guesswork here, but it's probably the case that filled_form.is_valid() is returning false, which will mean save() is never reached. To test this simply, just put an else and print on the if.
if filled_form.is_valid():
filled_form.save()
return redirect('index')
else:
print("Form validation failed")
print(filled_form.errors)
It's likely that you'd also want to return these errors to the user in the future, which means you'll need to make a couple of changes.
Right now, regardless of whether ObForm validates successfully, you are creating a new instance and passing that to the user. My typical approach would be to declare the form variable at the top of the function, and if it isn't already set when it comes to render the view, create a new instance of the form then. This way, if the form was populated by the user already (i.e. the request was a POST) then the errors will be returned with their input, instead of clearing their input (which is really annoying from a user's point of view!).
As a side note, I'm going to guess that you submitted a form with empty image and content fields, and were expecting that to be stored in your database? Try changing your field declarations to:
title = models.CharField(max_length=50)
image = models.ImageField(null=True, blank=True)
content = models.TextField(null=True, blank=True)
update_at = models.DateTimeField(auto_now=True)
null=True tells the database to allow null values, but blank=True tells the form validation to allow empty values.

Want to show detailview, clicking in a link in a listview

I want to show the detail from a school, which will show all the students but when i run the code, it doesnt find the url, i've been searching for the answer all over the web but still dont get it, the point is to show a list of all the schools in a list.html file, that works okay, but when i want to click in an item from that list, it supposed to show the details from that school which will be all the students attending that school, but it returns a 404 error, saying that it didnt find the url.
####MODELS
# Create your models here.
class School(models.Model):
name = models.CharField(max_length=256)
principal = models.CharField(max_length=256)
location = models.CharField(max_length=256)
def __str__(self):
return self.name
class Students(models.Model):
name = models.CharField(max_length=256)
age = models.PositiveIntegerField()
school = models.ForeignKey(School,on_delete=models.CASCADE,related_name='students')
def __str__(self):
return self.name
####VIEWS
from django.views.generic import View,DetailView,ListView
from .models import *
# Create your views here.
class Index(View):
def get(self,request):
return HttpResponse('Hello World')
class SchoolList(ListView):
model = School
template_name = 'firstapp/list.html'
context_object_name = 'School'
class SchoolDetails(DetailView):
model = School
template_name = 'firstapp/detail.html'
context_object_name = 'School_detail'
####URLS
from django.urls import path
from . import views
urlpatterns = [
path('list/',views.SchoolList.as_view(),name='list'),
path('School_detail/<int:pk>',views.SchoolDetails.as_view(),name='details')
]
####LIST HTML
{%extends 'firstapp/base.html'%}
{%block body_block%}
<h1>Here is a list of the schools!</h1>
{%for school in School%}
<p>{{school.name}}</p>
{%endfor%}
{%endblock%}
####DETAIL HTML
{%extends 'firstapp/base.html'%}
{%block body_block%}
{%for student in school_detail.students.all%}
<p>{{student.name}}</p>
{%endfor%}
{%endblock%}
Your link is not defined right
<p>{{school.name}}</p>
should be
<p><a href="{% url 'details' school.id%}>{{school.name}}</a></p>
Let me know if this solves the problem
This is one of the many reasons why using the {% url … %} template tag [Django-doc] is a good idea, because it is harder to make mistakes when you construct a URL in a template.
The URL should be School_detail/{{school.pk}}, but as said, it is better not to render this that way:
<p>{{school.name}}</p>
With this Django will look for a path(..) where the name='details', and replace the (first) parameter with school.pk.

Django get_absolute_url always throwing a 404

I am building a Django project ( 1.8) and am having trouble using get_absolute_url method of one of my models. I populated my database with some posts, so I could check my application. I am able to list the database objects in one of my views, so I know that the objects are there, and my app can display them. The problem comes when I click a link, and the view always returns a 404 ( basically It can't find an object to display)
Below is most of the code from this app. I tried to include my imports in these snippets, but truncated some of it to keep the post short.
valueFact/models.py
class ValueFactPost(models.Model):
STATUS_CHOICE = (
('draft', 'Draft'),
('published', 'Published')
)
title = models.CharField(max_length=250)
slug = models.SlugField(max_length=250, unique_for_date='publish')
author = models.ForeignKey(User, related_name='valueFact_posts')
publish = models.DateTimeField(default=timezone.now)
body = models.TextField()
created = models.DateTimeField(auto_now_add=True)
updated = models.DateTimeField(auto_now=True)
status = models.CharField(max_length=10,
choices=STATUS_CHOICE,
default='draft')
stockSymbol = models.ForeignKey('Symbol', null=True )
objects = models.Manager()
published = ValueFactManager()
def get_absolute_url(self):
return reverse('companies:valuefact_detail', args=[self.publish.year,
self.publish.strftime('%m'),
self.publish.strftime('%d'),
self.slug])
urls.py
from django.conf.urls import include, url
urlpatterns = [
url(r'^admin/', include(admin.site.urls)),
url(r'^companies/', include('valueFact.urls', namespace='companies', app_name='companies')),
]
valueFact/urls.py
from django.conf.urls import url
from . import views
urlpatterns = [
url(r'^$', views.valueFactListView.as_view(), name='valueFact_list'),
url(r'^(?P<year>\d{4})/(?P<month>\d{2})/(?P<day>\d{2})/(?P<post>[-\w]+)/$',
views.valuefact_detail,
name='valuefact_detail'),
]
valueFact/views.py
from django.shortcuts import render, get_object_or_404, render_to_response
from valueFact.models import ValueFactPost, Symbol
class valueFactListView(ListView):
queryset = ValueFactPost.published.all()
context_object_name = 'valueFacts'
paginate_by = 2
template_name = 'companies/valueFact/list.html'
def valuefact_detail(request, year, month, day, post):
post = get_object_or_404(ValueFactPost, slug=post,
status='published',
publish__year=year,
publish__month=month,
publish__day=day)
return render(request, 'companies/valuefact/detail.html', {'post':post})
The template where the problem seems to occur:
list.html
{% for post in valueFacts %}
<a href="{{ post.get_absolute_url }}">
{{ post.title}}
</a>
</h2>
<p class="date">
Published {{post.publish}} by {{post.author}}
</p>
{{post.body|truncatewords:30|linebreaks}}
{% endfor %}**
The error in the django console is : Not Found: /companies/2016/03/23/seadrill-debt/
I have spent an inordinate amount of time on this issue which would probably take someone a couple of minutes to fix. Thanks in advance.
Do you have PYTZ installed? I can't guarantee this is the answer, but it's worth a try. I don't see anything wrong with the code, as I'm using pretty much the same code in a project.
If you use different timezone than UTC, when an object is saved, publish attribute can be different than when you have UTC timezone.
If that's the case your get_absolute_url method has to return localtime version of publish.
In your models.py, consider writing like this
def get_absolute_url(self):
"""
Get single object.
year, month, day, and slug order.
"""
# convert to Asia/seoul time
localdatetime = timezone.localtime(self.publish)
return reverse(
"blog:post_detail",
args=[
localdatetime.year,
localdatetime.month,
localdatetime.day,
self.slug,
],
)
SO has other similar post regarding this.
Method 1:
Instead of using DateTimeField use DateField in your model and the problem will be solved.
# models.py
class Post(models.Model):
publish = models.DateField(default=timezone.now)
created = models.DateField(auto_now_add=True)
updated = models.DateField(auto_now=True)
Method 2: Override get_object
Your url pattern establishes 4 keyword arguments:
year
month
day
post
but your call to reverse is passing those values in as positional arguments (args), which is a list. Instead, pass them in as keyword arguments (kwargs), which is a dictionary:
def get_absolute_url(self):
return reverse('companies:valuefact_detail', args=[],
kwargs={
'year':self.publish.year,
'month': self.publish.strftime('%m'),
'day': self.publish.strftime('%d'),
'post': self.slug
})

Displaying both slug and ID in URL, but route by ID only in Django

What I'm trying to achieve is: my News app should display a slug, but only query the article by ID in the form of /news/24/this-is-the-slug
Unfortunately I'm getting a NoReverseMatch: Reverse for 'news_detail' with arguments '('',)' and keyword arguments '{}' not found. when trying to browse an article. The URL generated in the template looks correct as stated above (I can confirm this by doing a search via Haystack, which delivers the correct URL).
models.py
class News(models.Model):
id = models.IntegerField(primary_key=True, editable=False)
category = models.CharField(max_length=50L)
title = models.CharField(max_length=200L)
rss_summary = models.TextField(max_length=2000L)
body_text = models.TextField(max_length=5000L)
post_date = models.DateTimeField()
prettyurl = models.SlugField(max_length=100L)
class Meta:
db_table = 'news'
def __unicode__(self):
return self.title
def get_absolute_url(self):
return urlresolvers.reverse('news_detail', kwargs={'pk': self.id, 'slug': self.prettyurl })
urls.py
urlpatterns = patterns(
'',
url(
r'^$',
view=views.NewsListView.as_view(),
name='news_index'),
url(
r'^(?P<pk>\d+)/',
view=views.NewsDetailView.as_view(),
name='news_detail'),
url(
r'^(?P<pk>\d+)/(?P<slug>[-\w]+)/$',
view=views.NewsDetailView.as_view(),
name='news_detail'),
url(
r'^archive/$',
view=views.NewsArchiveIndexView.as_view(),
name="archive_month"),
[... more unrelated urls ...]
views.py
class NewsDetailView(DetailView):
#name='news_detail'),
model = News
context_object_name = 'news'
#slug_url_kwarg = 'prettyurl'
#slug_field = 'prettyurl'
template_name = 'news/detail.html'
Template
`<p>Permalink for this article.`
Thanks #Daniel Roseman and #yuvi. With your help I managed to solve my problem by defining the URL pattern to this:
r'^(?P<pk>\d+)(?:/(?P<slug>[\w\d-]+))?/$',
Which allows all my wanted forms of
news/nn
news/nn/
news/nn/a-slug
news/nn/a-slug/
In the template, I use
{% url 'news_detail' news.id news.prettyurl %}
Which shows the fourth version in the listing above.
Thanks again!
I'm not quite sure why you're bothering to capture the slug at all. Rather than having a named group in the URL pattern, you could just have one that ignores everything after the PK:
r'^(?P<pk>\d+)/.*',
which would work just as well whether or not you passed the slug, so you could then get rid of your duplicated patterns.
There are two basic problems with what you have, though. Firstly, even though you state you want to actually match only on PK, you don't even pass the PK to the URL, just the slug. Secondly, even the slug appears to be blank, as the error message states (the args variable is just '').
You should instead pass the actual PK:
{% url 'news_detail' news.pk %}