I have the following django model
class Course(models.Model):
name = models.CharField(max_length=50)
pre_req_courses = models.ManyToManyField('self')
def __str__(self):
return self.name
when I create courses in following way:
course1 = Course.objects.create(name='Course1')
course1.save()
course2 = Course.objects.create(name='Course2')
course2.save()
course2.pre_req_courses.set(course1)
when I run the following command I get:
course2.pre_req_courses.all()
>>> <QuerySet [<Course: Course1>]>
course1.pre_req_courses.all()
>>> <QuerySet [<Course: Course2>]>
Wht I want is:
course2.pre_req_courses.all()
>>> <QuerySet [<Course: Course1>]>
course1.pre_req_courses.all()
>>> <QuerySet []>
How can I achieve this
I couldn't solve it with the m2m field.
It's a little complicated, but you can get the result you want with the code below.
models.py
from django.db import models
class CourseRelation(models.Model):
low_level = models.ForeignKey(
'course.Course',
models.CASCADE,
related_name='relations_me_low'
)
high_level = models.ForeignKey(
'course.Course',
models.CASCADE,
related_name='relations_me_high'
)
def __str__(self):
return '%s -> %s' % (self.low_level, self.high_level)
class Course(models.Model):
name = models.CharField(max_length=50)
def __str__(self):
return self.name
#property
def pre_req_courses(self):
course_ids = CourseRelation.objects.filter(high_level=self).values('low_level')
return Course.objects.filter(id__in=course_ids)
#property
def next_courses(self):
course_ids = CourseRelation.objects.filter(low_level=self).values('high_level')
return Course.objects.filter(id__in=course_ids)
shell
>>> python_course = Course.objects.create(name='Python')
>>> web_course = Course.objects.create(name='Web')
>>> django_course = Course.objects.create(name='Django')
>>> CourseRelation.objects.create(low_level=python_course, high_level=django_course)
<CourseRelation: Python -> Django>
>>> CourseRelation.objects.create(low_level=web_course, high_level=django_course)
<CourseRelation: Web -> Django>
>>> python_course.pre_req_courses
<QuerySet []>
>>> python_course.next_courses
<QuerySet [<Course: Django>]>
>>> django_course.pre_req_courses
<QuerySet [<Course: Python>, <Course: Web>]>
>>> django_course.next_courses
<QuerySet []>
update
When I read Abdul Aziz Barkat's comment, I realized that a single line would solve it.
pre_req_courses = models.ManyToManyField(
'self',
related_name='next_courses',
symmetrical=False
)
Related
Considerer this model
class Dealership(models.Model):
dealership = models.CharField(max_length=50)
class Ordered(models.Model):
customer = models.ForeignKey("Customer")
dealership = models.ManyToManyField("Dealership")
status = models.CharField(max_length=2, choices=status_list, default='p')
I try
$ ./manage.py shell
>>> from new_way.core.models import Ordered, Dealership
>>> q = Ordered.objects.all()[:5]
>>> [i.dealership for i in q.dealership.all]
And generate error
Traceback (most recent call last):
File "<console>", line 1, in <module>
AttributeError: 'QuerySet' object has no attribute 'dealership'
How to return
Ordered.dealership.dealership
all dealership by Ordered.
You're very close:
Change:
[i.dealership for i in q.dealership.all]
to:
[dealership for dealership in q.dealership.all()]
Here's sample output from one of my model's M2M relationships on a project which demonstrates what you should see from the list comprehension. shared_with is an M2M field to a model called Profile:
>>> from polls.models import Poll
>>> polls = Poll.objects.all()
>>> for p in polls:
... print([sw for sw in p.shared_with.all()])
...
[<Profile: kerri>]
[<Profile: kerri>]
[]
[<Profile: jake>, <Profile: kerri>]
[<Profile: jake>, <Profile: kerri>]
[<Profile: jake>, <Profile: kerri>]
[<Profile: btaylor>]
[<Profile: jake>, <Profile: kerri>]
It should be:
q.dealership.all() #gives a list of objects
You can directly do this instead of using list comprehension(in the above ans).
Example: (Taken from docs)
from django.db import models
class Publication(models.Model):
title = models.CharField(max_length=30)
def __str__(self): # __unicode__ on Python 2
return self.title
class Meta:
ordering = ('title',)
class Article(models.Model):
headline = models.CharField(max_length=100)
publications = models.ManyToManyField(Publication)
def __str__(self): # __unicode__ on Python 2
return self.headline
class Meta:
ordering = ('headline',)
Create a couple of Publications:
p1 = Publication(title='The Python Journal')
p1.save()
p2 = Publication(title='Science News')
p2.save()
p3 = Publication(title='Science Weekly')
p3.save()
Now, create an Article and the associate the Article with a Publication:
a1 = Article(headline='NASA uses Python')
a1.save()
a1.publications.add(p1, p2)
a1.publications.add(p3)
a1.publications.all()
[<Publication: Science News>, <Publication: Science Weekly>, <Publication: The Python Journal>]
I found out that django 1.7 supports now update_or_create() method in queryset.
But when I try it, it is not working. There is no object created when I try this:
models.py
class Model1(models.Model):
name = models.CharField(max_length=100)
status = models.CharField(max_length=100)
user = models.ForeignKey(User, null=True, blank=True)
class Model2(models.Model)
model = models.ForeignKey(Model1, null=True, blank=True)
field1 = models.CharField(max_length=100)
field2 = models.CharField(max_length=100)
user = models.ForeignKey(User, null=True, blank=True)
views.py
def ModelUpdate(request)
model1 = get_object_or_404(Model1, pk=request.POST['lid'])
model1.status =2
model1.save()
#based on the model1, we want to create or update model2
datadict ={'field1' : 1, 'field2':2,}
model2, created = Model2.objects.update_or_create(model=model1,
defaults=datadict)
if created:
print('model2 obj created') #for checking purpose
else:
print('model2 obj not created')
return render(request,'updated.html', {'update':'updated'})
I can see the message model2 obj created but the object was not saved in the database. What could be the problem?
UPDATE
Turns out it was my mistake. In the Model2, I defined the save method without returning super(Model2, self).save(*args, **kwargs)
That's why it didn't save the objects.
Maybe you verifying in the wrong way, because I created project with your models and everything works fine.
In my case I'm verifying that object was created/updated with objects.get() and printing values:
In [1]: from app1 import models
In [2]: m1 = models.Model1.objects.create(name=1, status=1)
In [3]: m1
Out[3]: <Model1: Model1 object>
In [4]: datadict ={'field1' : 1, 'field2':2,}
In [6]: model2, created = models.Model2.objects.update_or_create(model=m1, defaults=datadict)
In [7]: model2, created
Out[7]: (<Model2: Model2 object>, True)
In [8]: models.Model2.objects.get().field1, models.Model2.objects.get().field2
Out[8]: (u'1', u'2')
In [9]: datadict ={'field1' : 3, 'field2':4}
In [10]: model2, created = models.Model2.objects.update_or_create(model=m1, defaults=datadict)
In [11]: model2, created
Out[11]: (<Model2: Model2 object>, False)
In [12]: models.Model2.objects.get().field1, models.Model2.objects.get().field2
Out[12]: (u'3', u'4')
and
In [13]: models.Model2.objects.get().model.id
Out[13]: 1
In [14]: models.Model1.objects.get().id
Out[14]: 1
In Django's many-to-many relationships extra fields, we can add extra fields, as the code below (from https://docs.djangoproject.com/en/dev/topics/db/models/#intermediary-manytomany), my question is how could I get the extra field easily, is it possible to not query on the intermediary table directly? the exactly example is:
>>> ringo = Person.objects.create(name="Ringo Starr")
>>> paul = Person.objects.create(name="Paul McCartney")
>>> beatles = Group.objects.create(name="The Beatles")
>>> m1 = Membership(person=ringo, group=beatles,
... date_joined=date(1962, 8, 16),
... invite_reason="Needed a new drummer.")
>>> m1.save()
>>> beatles.members.all()
[<Person: Ringo Starr>]
>>> p = beatles.members.all()[0]
>>> p
[<Person: Ringo Starr>]
I want to get the date_joined value of p, I know it can be done by
>>> Membership.objects.get(person=p, group=beatles).date_joined
datetime.date(1962, 8, 16)
but is it possible to get it simply just like:
>>> p.xxx(*args)
=============== models.py ================
from django.db import models
class Person(models.Model):
name = models.CharField(max_length=128)
def __str__(self): # __unicode__ on Python 2
return self.name
class Group(models.Model):
name = models.CharField(max_length=128)
members = models.ManyToManyField(Person, through='Membership')
def __str__(self): # __unicode__ on Python 2
return self.name
class Membership(models.Model):
person = models.ForeignKey(Person)
group = models.ForeignKey(Group)
date_joined = models.DateField()
invite_reason = models.CharField(max_length=64)
From the documentation page you link to:
Another way to access the same information is by querying the many-to-many reverse relationship from a Person object:
>>> ringos_membership = ringo.membership_set.get(group=beatles)
>>> ringos_membership.date_joined
datetime.date(1962, 8, 16)
>>> ringos_membership.invite_reason
'Needed a new drummer.'
Resuing Django example:
from django.db import models
class Publication(models.Model):
title = models.CharField(max_length=30)
class Article(models.Model):
headline = models.CharField(max_length=100)
publications = models.ManyToManyField(Publication)
How to I obtain all Articles that do not have publications?
Article.objects.filter(...)
Specify publications as None:
Article.objects.filter(publications=None)
Example:
>>> p1 = Publication.objects.create(title='1')
>>> p2 = Publication.objects.create(title='2')
>>> a1 = Article.objects.create(headline='a')
>>> a2 = Article.objects.create(headline='b')
>>> a3 = Article.objects.create(headline='c')
>>> a1.publications.add(p1)
>>> a1.publications.add(p2)
>>> a3.publications.add(p1)
>>> Article.objects.filter(publications=None)
[<Article: Article object>]
>>> _[0].headline
u'b'
Article.objects.filter(publications=None)
Define blank=True with publications field into Article model else it will not allow you to create article without publications.
publications = models.ManyToManyField(Publication, blank=True)
I have the following Bank model:
from django.db import models
class Bank(models.Model):
id = models.BigIntegerField(primary_key=True)
name = models.CharField(unique=True, max_length=255)
identifier = models.CharField(unique=True, max_length=255)
created_at = models.DateTimeField()
updated_at = models.DateTimeField()
class Meta:
db_table = u'bank'
app_name = 'mcif'
Now look at this:
>>> from mcif.models import *
>>> b = Bank()
>>> b.name = "wee"
>>> b.identifier = "wee"
>>> b.save()
>>> b.id
>>>
If I understand how Django works, that Bank object is supposed to get updated with the id of the saved record. Any idea why this isn't happening?
Its a problem in Django when dealing with BigInt as Autoincrementing PK. Maybe this code snippet helps you solve this issue. Its a makeshift solution