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
Related
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)
I want to access foreign key table's data into django templates.
my code is as below.
class TutorialCategory(models.Model):
tutorial_category = models.CharField(max_length=200)
category_summary = models.CharField(max_length=200)
category_slug = models.CharField(max_length=200, default=1)
class TutorialSeries(models.Model):
tutorial_series = models.CharField(max_length=200)
tutorial_category = models.ForeignKey(TutorialCategory, verbose_name="Category", on_delete=models.SET_DEFAULT)
series_summary = models.CharField(max_length=200)
Tutorial_obj = TutorialSeries.objects.get(pk=1)
{{ Tutorial_obj.tutorial_series}}
{{Tutorial_obj.category_summary}} // Not able to access TutorialCategory
I have searched on SO also & found to use _set which I have used but still not able to access table.
Pls if anyone have suggestion pls guide me .
You want
{{Tutorial_obj.tutorial_category.category_summary}}
Not sure if that was just a silly error or a misunderstanding of how it's supposed to work
BTW stick to the conventions: an instance really should be lower case tutorial or tutorial_obj if you insist.
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
I am building a simple forum application and I need some help with counting foreign key objects via through relation.
Let's say my models look like this:
class Forum(models.Model):
title = models.CharField(max_length=255)
description = models.TextField()
slug = models.SlugField(unique=True, blank=True)
class Thread(models.Model):
title = models.CharField(max_length=255)
forum = models.ForeignKey(to=Forum, related_name='threads')
slug = models.SlugField(unique=True, blank=True)
class Post(models.Model):
body = models.TextField()
author = models.ForeignKey(User)
thread = models.ForeignKey(to=Thread,related_name='posts')
Now we create forum object.
forum = Forum.objects.get(slug=forum)
We can count the number of threads inside forum like this: forum.threads.count()
My question is, how can i count all the posts in a forum?
I've tried something like all_posts = forum.thredas.posts.count() but as expected it didn't work.
Thanks!
Generally in Django it is a good principle that when you want to do something with a model, you should start your query from that model. So, since you need to count posts, you should start with the Post model.
From there you can use the double-underscore syntax to filter to the particular Forum you want.
forum_posts = Post.objects.filter(thread__forum=my_forum)
forum_post_count = forum_posts.count()
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)