Query ManyToMany relations without a named through field - python

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.

Related

Django Model Bidirectional Many to Many Declaration?

I have two models, article and publication, in which I declare a manytomany field within Article. However, I also want to have a reference from publication to articles as well. Is the best way to just declare another ManyToManyField, i.e. Articles = models.ManyToManyField('Article'), and if so, how do I make it so that it's linked to that linking table?
class Article(models.Model):
headline = models.CharField(max_length=100)
publications = models.ManyToManyField('Publication')
class Publication(models.Model):
title = models.CharField(max_length=30)
articles = ???
You are declaring the model in wrong way
correct way should be:
class Publication(models.Model):
title = models.CharField(max_length=30)
class Article(models.Model):
headline = models.CharField(max_length=100)
publication = models.ForeignKey(Publication, on_delete=models.CASCADE)

How to Connect a Django Model with ManyToMany Relationship?

I am making an app that is pretty much similar to google classroom in django.
I have a Course model and an assignment model, and I want to connect an assignment to the specified course.
These are my models
class Assignment(models.Model):
course = models.ForeignKey(Course, on_delete=models.CASCADE)
name = models.CharField(max_length=100)
date_created = models.DateTimeField(default=timezone.now)
class Course(models.Model):
title = models.CharField(max_length=100)
subject = models.CharField(max_length=100)
image = models.ImageField(default='no_course_image.jpg', upload_to='course_images')
owner = models.ForeignKey(User, on_delete=models.CASCADE)
students_invited = models.ManyToManyField(User, null=True, blank=True)
assignments = models.ManyToManyField(Assignment, null=True, blank=True)
date_published = models.DateTimeField(default=timezone.now)
class Meta:
verbose_name_plural = 'Course'
ordering = ['-date_published']
def __str__(self):
return '{} - {}'.format(self.title, self.owner)
But i am getting an error when I specify the course field in the assignment model with the ForeignKey!
Could you please help me with how to connect the assignment to the Course model?
Thank you
ForeignKey is used to setup a many to one relationship. As you are trying to setup a ManyToManyField it won't work in this situation as you can see in the Django documentation
ForeignKey¶
class ForeignKey(to, on_delete, **options)¶
A many-to-one relationship. Requires two positional arguments:
the class to which the model is related and the on_delete option.
In fact you don't even need to set the relation in the Assignment Model as Django will take care of creating a third table linking the two together by their primary keys. You can see this in the documentation
from django.db import models
class Publication(models.Model):
title = models.CharField(max_length=30)
class Meta:
ordering = ['title']
def __str__(self):
return self.title
class Article(models.Model):
headline = models.CharField(max_length=100)
publications = models.ManyToManyField(Publication)
class Meta:
ordering = ['headline']
def __str__(self):
return self.headline
So every time you add the assignment to the course like so
>>> c1 = Course(title='Python Course')
>>> c1.save()
>>> a1 = Assignment(name='Python Assignment')
>>> a1.save()
>>> c1.assignments.add(a1)
And the relation will automatically be created and c1.assignments.all() will return all the assignments linked to the course
If you need to go the other way around then you would use a1.course_set.add(c1). When using the model that doesn't have the ManyToManyField object tied to it you need to use the *_set notation where * will be replaced by the model name in lower case. Can read more about Related Objects references in the docs here
When you try to create the Model Assignment with reference to the model Course, the Course Model has not yet created and vice versa and you will get an error either of the model is not defined
You can use the quotes for it
class Assignment(models.Model):
course = models.ForeignKey('Course', on_delete=models.CASCADE)
name = models.CharField(max_length=100)
date_created = models.DateTimeField(default=timezone.now)
You can use a custom through model enter link description here
I guess the Course model has to be written before the Assignment model.

Django unique together relationship with field and manytomany on self

I'm try create post with language and content, and relate it on other versions of same page, but I'm get stuck
class Page(models.Model):
content = models.TextField()
language = models.CharField(max_length=7, choices=settings.LANGUAGES)
versions = models.ManyToManyField('self', blank=True)
class Meta:
unique_together = ('language', 'versions',)
This will not work properly, because Django not allow make "unique" ManyToMany fields.
Then I'm try make same relationship trough related model:
class VersionsPage(models.Model):
pass
# ToDo: add unique together here, to foreign key field
class Page(models.Model):
...
versions = models.ManyToManyField('self', blank=True, through="VersionsPage")
Anyone know how to make that without using symmetrical=False?
I think you are looking for something like this:
class Page(models.Model):
pass
class PageVersion(models.Model):
page = models.ForeignKey(Page, related_name='versions')
content = models.TextField()
language = models.CharField(max_length=7, choices=settings.LANGUAGES)
class Meta:
unique_together = ('page', 'language',)
#getting all page versions:
page = Page.objects.get(pk=some_id)
versions = page.versions.all()

Fetching objects in complex through relationship Django

I have been trying numerous things to solve the following in Django object filtering but thus no luck.
Utalizing a primary key for an individual, I would like to build an object list of all their milestones.
My models are laid out as such:
TEAM MEMBER -- owns many tasks through --> OWNER -- -- > TASKS -- each task has multiple milestones --> MILESTONES
The models are shown below:
class TeamMember(models.Model):
member_name = models.CharField(max_length=140)
email = models.EmailField()
task= models.ManyToManyField(Task, through='Owner')
class Owner(models.Model):
teammember = models.ForeignKey(TeamMember, on_delete=models.CASCADE)
task = models.ForeignKey(Task, on_delete=models.CASCADE)
class Task(models.Model):
task_name = models.CharField(max_length=140)
task_description = models.TextField(null=True, blank=True)
last_updated = models.DateTimeField(null=True, blank=True)
class Milestone(models.Model):
name = models.CharField(max_length=140)
task= models.ForeignKey(Task, on_delete=models.CASCADE)
expected_date = models.DateTimeField()
The following doesnt work for me:
Milestones.objects.filter(teammember__id)
Any ideas?
You need to use multiple double.underscore steps. Actual code depends on your exact model and field names.
Something like:
Milestones.objects.filter(task__owner__teammember__id=1)
I you want to get milestone data for a particular member , you can get it by defining the fields in value queryset as follows:
TeamMember.objects.filter(id=1).values
('task__milestone','owner__task__milestone',
'task__milestone__expected_date','task__milestone__name')
I did not test it , but surely it will get all the related information.

How to get all objects referenced as ForeignKey from given field in a module in django

I have two classes: Author and Book. I want class Authors to have an attribute that contains all books written by the said author, as referenced to as foreign key in the class Books. The method I did does not appear to be working, which I assume is because when the database is being created in migrations, no Books objects exist yet. Or so I believe, I'm pretty new at django.
class Author(models.Model):
AuthorName = models.CharField(max_length=255, unique=True)
books = Book.objects.get(pk=object_instance.pk)
class Book(models.Model):
BookName = models.CharField(max_length=255)
Author = models.ForeignKey('Author')
The error message I get is:
NameError: name 'Book' is not defined
Which I get, is because I'm referencing to another class without actually having and instance of that class. I just can't figure out a proper way to do this.
EDIT: I reformatted it to be like this:
class Author(models.Model):
AuthorName = models.CharField(max_length=255, unique=True)
books = author.book_set.all()
class Book(models.Model):
BookName = models.CharField(max_length=255)
Author = models.ForeignKey('Author')
which yields error:
NameError: name 'author' is not defined
Maybe I should just query for the datapoints I need later on in views as opposed to creating own field for them in models though..
EDIT 2: solution from answers:
So my mistake all along was to try to add the "books" field in the author table. I guess there's no way to do this then. I can get that method to work in views so I guess this is sort of solved, although not in the way I was originally planning to do it.
doing
class Author(models.Model):
AuthorName = models.CharField(max_length=255, unique=True)
class Book(models.Model):
BookName = models.CharField(max_length=255)
Author = models.ForeignKey('Author')
and then later doing this in views:
author = Author.objects.get(pk=1)
books = author.book_get.all()
yields the wanted result (which I sort of knew beforehand, but I was trying to implement a books field in the models, which, if i correctly understood, is not possible at least not with this method).
another solution:
class Author(models.Model):
AuthorName = models.CharField(max_length=255, unique=True)
class Book(models.Model):
BookName = models.CharField(max_length=255)
Author = models.ForeignKey(Author, related_name = "books")
You don't need to create a separate field in Authors model
class Author(models.Model):
AuthorName = models.CharField(max_length=255, unique=True)
class Book(models.Model):
BookName = models.CharField(max_length=255)
Author = models.ForeignKey('Author')
You can get all books of a particular author like:
author = Author.objects.get(id=1)
books = author.book_set.all()
Learn more about backward relationships here
Just add related_name to ForeignKey and you will be able to get all books made by an author.
For example:
class Book(models.Model):
...
author = models.ForeignKey('Author', related_name='books')
...
and later...
author = Author.objects.get(pk=1)
books = author.books.all()
You did something weird in line:
books = Book.objects.get(pk=object_instance.pk)
Just delete it. You will be able to use author.book_set. You can also use related_name parameter of ForeignKey.
See the Django docs: https://docs.djangoproject.com/en/4.0/ref/models/fields/#foreignkey

Categories