How to make django all urls to be top level slug?
Top level slug I mean that all urls has unique slug example:
example.com/articles
example.com/article-1
example.com/article-2
example.com/article-3
example.com/reviews
example.com/reviews-1
example.com/reviews-2
but not:
example.com/articles/article-1
example.com/articles/article-2
example.com/articles/article-3
example.com/reviews/reviews-1
example.com/reviews/reviews-2
I have a lot of apps like articles, reviews, and other custom pages.
So, what you think about this aproach that I create app with model like this:
class Link(models.Model):
slug = models.SlugField(unique=True)
and then I will use it in my articles model like this:
from links.models import Link
class Article(models.Model):
title = models.CharField()
slug = models.OneToOneField(
Link,
on_delete=models.CASCADE,
primary_key=True,
)
body = models.TextField()
.
from links.models import Link
class Review(models.Model):
title = models.CharField()
slug = models.OneToOneField(
Link,
on_delete=models.CASCADE,
primary_key=True,
)
review = models.TextField()
and then I will have only one url field in my mane urls.py file:
url(r'^(?P<slug>[-_\w]+)', views.link, name='link'),
And now how I should filter data where I want to return article or review?
Like this or maybe there exists better solution?
from django.http import HttpResponseRedirect
from .models import Link
from articles.models import Article
from review.models import Review
def link(request, link):
link = Link.objects.get(link=link)
if Article.objects.filter(slug=Link).exists():
link = link.slug
return HttpResponseRedirect(link)
if Review.objects.filter(slug=Link).exists():
link = link.slug
return HttpResponseRedirect(link)
return HttpResponseRedirect('/')
I need that for google because if I decide one day to change /articles to /blog then I will break hundreds of urls in google search.
Your idea is almost perfect. Only changes I recommend:
1) There doesn't seem to be need for a Link model. You slug can be a CharField inside the Article model itself
class Article(models.Model):
title = models.CharField()
slug = models.CharField(max_length=255, unique=True)
body = models.TextField()
2) Reviews belong to articles. So instead of Review having a ForeignKey to this Link object which should no longer exist, it should have a ForeignKey to Article
Related
I'm trying to hide and delete two fields from showing in a form I created in the Django administration page using ModelForm.
I looked at answers that said I should use the "exclude" meta field, but I don't know why it's not working in my case.
Here is my code:
models.py:
class Activity(models.Model):
type = models.CharField(max_length=50, default="")
title = models.CharField(max_length=200, default="")
description = models.CharField(max_length=500)
owner = models.ForeignKey(User, related_name="owner")
college = models.CharField(max_length=200)
location = models.CharField(max_length=200)
room = models.CharField(max_length=200)
startDate = models.DateTimeField(null=True, blank=True)
endDate = models.DateTimeField(null=True, blank=True)
attendee = models.ManyToManyField(Attendee, related_name="attendees",null=True, blank=True)
volunteer = models.ManyToManyField(Volunteer, related_name="volunteers",null=True, blank=True)
I'm trying to exclude the "attendee & volunteer" fields from displaying in the Django administration form.
In admin.py I have:
from django.contrib import admin
from django import forms
from KSUvity.models import Activity
class ActivityForm(forms.ModelForm):
class Meta:
model = Activity
exclude = ['attendee', 'volunteer',]
class ActivityAdmin(admin.ModelAdmin):
exclude = ['attendee', 'volunteer',]
form = ActivityForm
admin.site.register(Activity, ActivityAdmin)
You have to create an admin.py file in your app and register your models
Follow the instuctions
See the example below
from django import forms
from django.contrib import admin
from myapp.models import Person
class PersonForm(forms.ModelForm):
class Meta:
model = Person
exclude = ['name']
class PersonAdmin(admin.ModelAdmin):
exclude = ['age']
form = PersonForm
admin.site.register(Person, PersonAdmin)
You can use either fields or exclude in one class.
In your app admin field add this code.
app_name/admin.py
from django.contrib import admin
class ActivityAdmin(admin.ModelAdmin):
exclude = ('attendee', 'volunteer',)
You have to use ModelAdmin option to exclude fields from form in Django administration, either ModelAdmin.exclude or ModelAdmin.fields. Below is an example:
class ActivityAdmin(admin.ModelAdmin):
exclude = ('attendee', 'volunteer', )
To make it work, you register model like this:
admin.site.register(Activity, ActivityAdmin)
You add this code to admin.py file.
I want to be able to add some blog posts with categories and some without categories in django. with this models django admin won't let me add blog posts without a category. Thanks.
from django.db import models
from django.db.models import permalink
class Blog(models.Model):
title = models.CharField(max_length=100, unique=True)
slug = models.SlugField(max_length=100, unique=True)
body = models.TextField()
pub_date = models.DateField(db_index=True, auto_now_add=True)
# Many-to-one relationship.
category = models.ForeignKey('blog.Category')
class Category(models.Model):
title = models.CharField(max_length=100)
slug = models.SlugField(max_length=100)
Update your model like this:
category = models.ForeignKey('blog.Category', blank=True, null=True)
blank=True allow forms to have a blank value.
null=True allows a null value in the database.
Edit: here is the documentation
I have two apps news and article which both have exactly the same model name Comment:
class Comment(models.Model):
author = models.ForeignKey(User)
created = models.DateTimeField(auto_now_add=True)
title = models.CharField(max_length=100, default='', blank=True)
body = models.TextField()
post = models.ForeignKey(Photo)
published = models.BooleanField(default=True)
Now, in a view I want to delete certain comments from both apps:
Comment.objects.filter(author=someauthor).delete()
How can I achieve that without changing the model names?
You can use import ... as ... so that both model names do not conflict:
from news.models import Comment as NewsComment
from article.models import Comment as ArticleComment
...
NewsComment.objects.filter(author=someauthor).delete()
ArticleComment.objects.filter(author=someauthor).delete()
I have a very simple app which at the moment declares two models: one is called "Content" and simply holds content data and the other is "Page" which includes "Content" as a OneToOneField.
The reason I've done this is so that I can have "Page" as an actual concrete class that I use and when other models in other modules I'm planning need page data, they can simply include "Content" as a OneToOneField. I've done it this way to avoid inheritance and use composition instead.
models.py:
from django.db import models
class Content(models.Model):
"""Basic page data which can be used by other modules"""
title = models.CharField(max_length=200)
html_title = models.CharField(max_length=200)
meta_desc = models.CharField(max_length=200)
keywords = models.CharField(max_length=200)
content = models.TextField()
class Page(models.Model):
"""Concrete implementation of a basic page managed by the admin"""
slug = models.SlugField()
content = models.OneToOneField(Content)
def __str__(self):
return self.content.title
admin.py:
from django.contrib import admin
from content.models import Page, Content
class ContentInline(admin.TabularInline):
model = Content
fields = ('title', 'html_title', 'meta_desc', 'keywords', 'content')
class PageAdmin(admin.ModelAdmin):
fields = ('slug',)
inlines = [ContentInline]
On the page admin I get this exception:
Exception at /admin/content/page/add/
<class 'content.models.Content'> has no ForeignKey to <class 'content.models.Page'>
What is says of course is correct, but I cannot seem to find a way of doing what I want, which is to include an inline of the non-defining side of a relationship. I don't want to declare the relationship on "Content" as then I'd have to define every single relationship to it inside that class which would introduce dependencies to other modules, which in my opinion it should know nothing about.
Using Django 1.6 on Python 3.3.
Edit: As indicated in the comments, I've decided to use inheritance. My initial concern about this was that I wanted the flexibility to be able to compose classes from multiple other classes. However, since the Django ORM does support multiple inheritance and if I'd realised that method was called "mixins" (new to Python) I would have got somewhere a lot sooner.
Example mixins with models:
from django.db import models
class Content(models.Model):
"""Basic page data which can be used by other modules"""
title = models.CharField(max_length=200)
html_title = models.CharField(max_length=200)
meta_desc = models.CharField(max_length=200)
keywords = models.CharField(max_length=200)
content = models.TextField()
def __str__(self):
return self.title
class Meta:
abstract = True
class Data(models.Model):
data_name = models.CharField(max_length=200)
class Meta:
abstract = True
class Page(Content, Data):
"""Concrete implementation of a basic page managed by the admin"""
slug = models.SlugField()
And then I can just use it as one model in admin.py.
Another solution is moving the OneToOneField from Content to Page
class Content(models.Model):
"""Basic page data which can be used by other modules"""
title = models.CharField(max_length=200)
html_title = models.CharField(max_length=200)
meta_desc = models.CharField(max_length=200)
keywords = models.CharField(max_length=200)
content = models.TextField()
page = models.OneToOneField(Page, primary_key=True, related_name="content")
class Page(models.Model):
"""Concrete implementation of a basic page managed by the admin"""
slug = models.SlugField()
def __str__(self):
return self.content.title
You can still do page.content and the inline form will work out of the box
EDIT:
One cons of that approach is that it will allow the user to create a page without assigning any content to it (in which case page.content will crash)
Its very easy to overcome this issue by creating custom form
class ContentAdminForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
kwargs["empty_permitted"] = False
super(ContentAdminForm, self).__init__(*args, **kwargs)
Then in the admin page
class ContentInline(admin.TabularInline):
model = Content
form = ContentAdminForm
fields = ('title', 'html_title', 'meta_desc', 'keywords', 'content')
If you don't want to change your models at all, there's a django module to display the non-defining side inline: django_reverse_admin
You'll need to add django_reverse_admin to your requirements.txt:
-e git+https://github.com/anziem/django_reverse_admin.git#egg=django_reverse_admin
Then import it:
admin.py
from django.contrib import admin
from django_reverse_admin import ReverseModelAdmin
from content.models import Page, Content
# don't need to define an inline anymore for Content
class PageAdmin(ReverseModelAdmin):
fields = ('slug',)
inline_reverse = ['content']
inline_type = 'tabular' # or could be 'stacked'
I have this setup in my models:
class Author(models.Model):
name = models.CharField(max_length=100)
class Topic(models.Model):
name = models.CharField(max_length=100)
class Article(models.Model):
name = models.CharField(max_length=100)
authors = models.ManyToManyField(Author, null=True, blank=True)
topics = models.ManyToManyField(Topic, null=True, blank=True)
Given an author, I want to know which topics he wrote about:
def author_info(request, pk):
author = get_object_or_404(Author, pk=pk)
topics = ????
If I had specified a through field, I could use that, but now Django makes the through field for me, and since its supposed to be transparent, Id rather not reference the field (unless there is a proper Django construction for that).
Use Lookups that span relationships:
topics = Topic.objects.filter(article__authors=author).distinct()
Note: you have to use distinct here, because the same topic can be selected by different articles.