Django inheritance - python

Please have a look:
class Categorie(models.Model):
id = models.AutoField('id', primary_key=True)
title = models.CharField('title', max_length=800)
articles = models.ManyToManyField(Article)
class Article(models.Model):
id = models.AutoField('id', primary_key=True)
title = models.CharField('title', max_length=800)
slug = models.SlugField()
indexPosition = models.IntegerField('indexPosition', unique=True)
class CookRecette(Article):
ingredient = models.CharField('ingredient', max_length=100)
class NewsPaper(Article):
txt = models.CharField('ingredient', max_length=100)
So I created "CookRecette" and "NewsPaper" as "Article".
I Also create a "Categorie" class who link to (manyToMany) "Article".
But in the admin interface, I can't link from "Categorie" to an "CookRecette"or "NewsPaper".
Same from the code.
Any help ?
Cheers,
Martin Magakian
PS: I'm so sorry but actually this code is correct! So everything is working fine, I can see my "CookRecette"or "NewsPaper" from "Categorie"

I'll start by saying that you don't need to define the 'id' field, if you don't define it then Django will add it automatically.
Secondly, the CookRecette and NewsPaper objects are not linked to the Categorie object by any means (ForeignKey, OneToOne, OneToMany, ManyToMany) so they wouldn't be able to be accessed that way anyway.
After you have linked the models together in whichever way you wish, you might want to have a look at http://docs.djangoproject.com/en/1.2/ref/contrib/admin/#django.contrib.admin.InlineModelAdmin which will show you how to quickly edit related objects in the Djano admin console.

NewsPaper has part of it as Article object. If you will create new NewsPaper object, you will see a new object in articles. So in admin interface, when managing Categories, you will be able to select any article, and some of them are NewsPaper.
You can add news paper to a category like this:
category = Categorie(title='Abc')
category.save()
news_paper = NewsPaper(slug='Something new', indexPosition=1, txt='...')
news_paper.save()
category.articles.add(news_paper)
You can retrieve news papers from specific category like this:
specific_category = Categorie.objects.get(title='Abc')
NewsPaper.objects.filter(categorie_set=specific_category)

Related

Django Models - One to Many or Many o Many

In Django there are no One-to-Many relationships, there are just Many-to-One. In cases where defining Foreign Key on the child table is odd, should we go for Many-to-Many?
For example:
Book has many pages. If we define foreign key on the Page model, then the page has one book which is not an intuitive thing to do (or say).
OPTION 1:
class Book(models.Model):
name = models.CharField(max_length=100)
date_published = models.DateTimeField(default=timezone.now)
class Page(models.Model):
page_number = models.IntegerField()
page_text = RichTextField()
book = models.ForeignKey(Book, on_delete=models.CASCADE)
OPTION 2
class Book(models.Model):
name = models.CharField(max_length=100)
date_published = models.DateTimeField(default=timezone.now)
pages = models.ManytoMany(Page)
class Page(models.Model):
page_number = models.IntegerField()
page_text = RichTextField()
In option2 I can access the pages of the book by book.pages.
In option 1 I don't know how to access the pages, maybe book.pages_set.objects.all() which is not pretty.
I asked a fellow programmer and he said just use Many-to-Many. I understand what each approach means and I also understand the difference between the two as to what's happening in the database. My question is what is a better/standard approach.
I prefare the first option because many books doesn't have common page and its contains.
So, using first option you can access book pages using following queryset
pages = book.page_set.all()
Here, you can also use related_name parameter in foreignkey field as:
book = models.ForeignKey(Book, on_delete=models.CASCADE, related_name="pages")
then book pages can be fetched using
pages = book.pages.all()
I would like to use first example. As Books do not have common pages. For connecting Book to Page, you have to use backward relationship. In this relationships, _set method is used. All the model name & filed name must be small letter although they are in capital letter.
m=book.page_set.all()
Now you can use all attribute of Page from Book using m.
For more about reverse or backward relationship visit here
In your view you pass the id of the Book and you use it in your query to filter the pages they are linked to it.
for example:
def show_pages(request, book_id):
pages = Pages.objects.filter(book=book_id)

How can I construct a Django model whose fields are dependent upon another model's fields

I currently am making a simple newspaper Django application, and am working on the Articles model which looks as follows:
class Article(models.Model):
title = models.CharField(max_length=255)
body = models.TextField()
#date = models.DateTimeField(auto_now_add=True)
author = models.ForeignKey(
get_user_model(),
on_delete=models.CASCADE,
)
topic = models.CharField(max_length=255)
score_choices = [(-5,'-5'), (-4, '-4'), (-3,'-3'), (-2, '-2'), (-1,'-1'),
(0,'0'),(1,'1'), (2,'2'), (3,'3'), (4,'4'), (5,'5')]
score = models.IntegerField(choices=score_choices, default=0)
I am attempting to create another model that looks something like this:
class Topic(models.Model):
topic_name = models.CharField(max_length=255)
average_score =
Within the topic model, what I would like to do is somehow query all the Articles that have topic_name as their topic, and then return a list of scores that have been entered for Articles with that topic.
I'm currently pretty lost on this issue and I'm not even sure anymore if using the Django models is the best route. I've been reading through the Django Documentation as well as Third-Party books for a while but I can't find any reference here.
To summarize, I have two models: Article and Topic. Article has a field called 'topic' as well and I would like to create a field for my Topic class that is a function of the score field for all Article objects whose 'topic' field agrees with that of my separate Topic class. I apologize if this is confusing and I don't know all the terminology as I am trying to teach myself.
I have read through Django Documentation's pages on Models, Queries, Many-to-Many Relationships and various other properties. I still am unsure as to the solution.
Something like the following would work, using aggregation:
from django.db.models.aggregates import Avg
class Topic(models.Model):
topic_name = models.CharField(max_length=255)
def average_score(self):
return Article.objects.filter(topic=self.topic_name).aggregate(avg=Avg('score')).get('avg')

Create a Blog which support multiple type of post

I am a new user of Django, and I am trying to figure out how to created a model which can support many kind (type) of elements.
This is the plot : I want to create a Blog module on my application.
To do this, I created a model Page, which describe a Blog Page. And a model PageElement, which describe a Post on the blog. Each Page can contain many PageElement.
A PageElement can have many types, because I want my users could post like just a short text, or just a video, or just a picture. I also would like (for example) the user could just post a reference to another model (like a reference to an user). Depending of the kind of content the user posted, the HTML page will display each PageElement in a different way.
But I don't know what is the right way to declare the PageElement class in order to support all these cases :(
Here is my Page model :
class Page(models.Model):
uuid = models.UUIDField(default=uuid.uuid4, editable=False, unique=True)
# Basical informations
title = models.CharField(max_length=150)
description = models.TextField(blank=True)
# Foreign links
user = models.ForeignKey(
settings.AUTH_USER_MODEL,
on_delete=models.SET_NULL,
null=True,
related_name='pages_as_user'
)
created_at = models.DateTimeField(default=timezone.now)
# Other fields ....
class Meta:
indexes = [
models.Index(fields=['uuid']),
models.Index(fields=['user', 'artist'])
]
For now, I have two solutions, the first one use inheritance : When you create a new post on the blog, you create an Element which inherit from PageElement model. Here are my different Models for each cases :
class PageElement(models.Model):
page = models.ForeignKey(
Page,
on_delete=models.CASCADE,
related_name='%(class)s_elements'
)
updated_at = models.DateTimeField(default=timezone.now)
created_at = models.DateTimeField(default=timezone.now)
class PageImageElement(PageElement):
image = models.ImageField(null=True)
image_url = models.URLField(null=True)
class PageVideoElement(PageElement):
video = models.FileField(null=True)
video_url = models.URLField(null=True)
class PageTextElement(PageElement):
text = models.TextField(null=True)
class PageUserElement(PageElement):
user = models.ForeignKey(
'auth.User',
on_delete=models.CASCADE,
related_name='elements'
)
This solution would be the one I have choosen if I had to work with "pure" Python. Because I could stored each PageElement in a dictionnary and filter them by class. And this solution could be easily extended in the futur with new type of content.
But with Django models. It seems that is not the best solution. Because it will be really difficult to get all PageElement children from the database (I can't just write "page.elements" to get all elements of all types, I need to get all %(class)s_elements elements manually and concatenate them :/). I have thinked about a solution like below (I don't have tried it yet), but it seems overkilled for this problem (and for the database which will have to deal with a large number of request):
class Page(models.Model):
# ...
def get_elements(self):
# Retrieve all PageElements children linked to the current Page
R = []
fields = self._meta.get_fields(include_hidden=True)
for f in fields:
try:
if '_elements' in f.name:
R += getattr(self, f.name)
except TypeError as e:
continue
return R
My second "solution" use an unique class which contains all fields I need. Depending of the kind of PageElement I want to create, I would put type field to the correct value, put the values in the corresponding fields, and put to NULL all other unused fields :
class PageElement(models.Model):
page = models.OneToOneField(
Page,
on_delete=models.CASCADE,
related_name='elements'
)
updated_at = models.DateTimeField(default=timezone.now)
created_at = models.DateTimeField(default=timezone.now)
TYPES_CHOICE = (
('img', 'Image'),
('vid', 'Video'),
('txt', 'Text'),
('usr', 'User'),
)
type = models.CharField(max_length=60, choices=TYPES_CHOICE)
# For type Image
image = models.ImageField(null=True)
image_url = models.URLField(null=True)
# For type Video
video = models.FileField(null=True)
video_url = models.URLField(null=True)
# For type Text
text = models.TextField(null=True)
# For type User
user = models.ForeignKey(
'auth.User',
on_delete=models.CASCADE,
related_name='elements',
null=True
)
With this solution, I can retrieve all elements in a single request with "page.elements". But it is less extendable than the previous one (I need to modify my entire table structure to add a new field or a new kind of Element).
To be honnest, I have absolutly no idea of which solution is the best. And I am sure other (better) solutions exist, but my poor Oriented-Object skills don't give me the ability to think about them ( :( )...
I want a solution which can be easily modified in the future (if for example, I want to add a new Type "calendar" on the Blog, which reference a DateTime). And which would be easy to use in my application if I want to retrieve all Elements related to a Page...
Thanks for your attention :)
I'm not sure it fits your problem but using GenericForeignKeys/ContentType framework may be appropriate in this case. It's quite powerful when one grasps the concept.
Example construct:
class Page(models.Model):
content_type = models.ForeignKey(ContentType)
object_id = models.PositiveIntegerField()
page_element = GenericForeignKey('content_type', 'object_id')
...
You can now connect any model object by the GenericFK to the Page model. So adding a new type (as a new model), at a later stage, is not intrusive.
Update:
As a comment pointed out this construct doesn't support many PageElements in a good way for a Page.
To elaborate, one way to solve that problem, still taking advantage of the GenericFK...
class PageElement(models.Model):
class Meta:
unique_together=('page', 'content_type', 'object_id') # Solve the unique per page
page = models.ForeignKey(Page, related_name='page_elements')
content_type = models.ForeignKey(ContentType)
object_id = models.PositiveIntegerField()
content_object = GenericForeignKey('content_type', 'object_id')
A Page can have many "abstract" PageElements and content_object is the "concrete PageElement model/implementation".
Easy to retrieve all elements for a specific page and allows inspection of the ContentType to check the type of element etc.
Just one way of many to solve this particular problem.
To establish the relationship between Page and PageElement in Django you would rather use Foreign Key relationship, than inheritance.
class PageImageElement(PageElement):
page = models.ForeignKey(Page,
on_delete=models.CASCADE,
related_name='images')
image = models.ImageField(null=True)
image_url = models.URLField(null=True)
Every user's post would create an instance of Page. Every addition of image to the Page would create an instance of PageImageElement and you could query for them using the related name. This way would be really easy to access all video, image, text modules of a single Page.
On a related note, I would say that PageElement class could be abstract see the docs and if you declare fields as possibly containing null values as in video = models.FileField(null=True) then it might be worth declaring blank=True as well, otherwise there will be errors when creating the object with these fields undefined. Discussed, for example, here: differentiate null=True, blank=True in django
I can't just write "page.elements" to get all elements of all types
Well actually, you can if you use multi-table inheritance. The problem is that all records returned are instances of PageElement, meaning you lose all information of the subclass type and the additional data these child objects may hold.
There are quite a lot of packages that tackle this polymorphism problem:
django packages: Model inheritance

Listing foreign keys related to a proxy model in django admin

My models.py looks like this :
class Author(models.Model):
name = models.CharField()
class DraftBooks(models.Model):
title = models.CharField()
author = models.ForeignKey(Author)
status_choices = ((1,Draft),(2,Published))
status = models.SmallIntegerField(choices=status_choices)
class PubBooks(DraftBooks):
class meta:
proxy = True
verbose name = 'Published Books'
I am using a proxy model since I want a different change list view for books in draft state and books which have been published.To achieve this,my admin.py looks like this :
class AuthorAdmin(admin.ModelAdmin):
pass
admin.site.register(Author, AuthorAdmin)
class DraftBooksAdmin(admin.ModelAdmin):
list_display = ('title','author','status')
def queryset(self):
return DraftBooks.objects.filter(status='1')
admin.site.register(DraftBooks, DraftBooksAdmin)
class PubBooksAdmin(admin.ModelAdmin):
list_display = ('title','author','status')
def queryset(self):
return PubBooks.objects.filter(status='2')
admin.site.register(PubBooks, PubBooksAdmin)
This setup works perfectly fine.In my admin,now I have 3 change list views,one which shows a list of all authors,one which shows book which are in a draft state and finally one which shows the list of books which are in a published state.
I now need to add a hyperlink to every item (Author) in the authors' list overview that links to a view showing all books of the specific authors.For Example:
J.K. Rowling (books)
J.R.R. Tolkien (books)
where books is a hyperlink to a site showing all books of a particular author.
Now I am completely clueless as to how to do this.Django Xadmin has a plugin which provides just this feature.This Stackoverflow question also provides answer to this problem.But the problem is that they do not work in proxy models with the custom filters that I have.When I try to get the list of books by an author,I get only the books which are in Draft state.I would ideally want all the books,Draft and Published by an author.How do i achieve this ?

Django One-To-Many Models

The following models describe a vulnerability and the URLs out on the internet that reference that vulnerability. Assume that each URL only ever talks about 1 vulnerability, and that many URLs will discuss that vulnerability. Is this the correct way to lay out the model?
class Vuln(models.Model):
pub_date = models.DateTimeField("Publication Date")
short_description = models.CharField("Description", max_length=70)
reference_urls = models.ForeignKey(Url, unique=True, blank=True, verbose_name="Reference URLs")
vendor = models.ForeignKey(Vendor, verbose_name="Vendor")
class Url(models.Model):
url = models.URLField("URL", max_length=200)
The Admin application gives a 'select' box for the reference URLs, which isn't what I want. When I add a new vulnerability object, all of the existing URLs that have been entered show up in that dropdown, which is again unnatural. I feel like this should behave very similar to how a blog comment would, ie. the comment applies to a single blog entry and none other and that one blog entry may have many comments. How do I express this in a Django model?
It should be more like this:
class Vuln(models.Model):
pub_date = models.DateTimeField("Publication Date")
short_description = models.CharField("Description", max_length=70)
vendor = models.ForeignKey(Vendor, verbose_name="Vendor")
class Url(models.Model):
url = models.URLField("URL", max_length=200)
vulnerability = models.ForeignKey(Vuln)
If you're saying each Url talks about a specific vulnerability, then there is your relation in the Django DBM :)
As for the vendor field, you simply add another class, much like Class Vuln. For example:
class Vendor(models.Model):
field_names_go_here = models.TextField(max_length=70)
short_description = models.CharField("Description", max_length=70)
Hope this helps!
Regards, Alex

Categories

Resources