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.'
Related
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
)
I have a class B that contains a ForeignKey relation to class A. When I instantiate B I can access field 'a' but the reverse relation (which should be created automatically) raises an error. Concretely, I have the following class definitions:
from django.db.models import Model, CharField, DateField, ForeignKey
from django.urls import reverse
class Patient(Model):
GENDER = (
('M', 'Male'),
('F', 'Female'),
('U', 'Unknown'),
)
last_name = CharField(max_length=128, null=False)
first_name = CharField(max_length=128, null=False, default='')
gender = CharField(max_length=1, choices=GENDER, null=False)
dob = DateField(null=False)
def get_absolute_url(self):
return reverse('patient_detail', args=[str(self.id)])
def __str__(self):
return '{}, {} ({}, {})'.format(self.last_name, self.first_name, self.gender, self.dob)
class AttributeSet(Model):
name = CharField(max_length=128, null=False)
description = CharField(max_length=256, blank=True, default='')
def get_absolute_url(self):
return reverse('attribute_set_detail', args=[str(self.id)])
def __str__(self):
return self.name
class AttributeSetInstance(Model):
patient = ForeignKey('Patient', null=False) # Automatic 'attribute_set_instance_set' backward relation?
attribute_set = ForeignKey('AttributeSet', null=False)
def get_absolute_url(self):
return reverse('attribute_set_instance_detail', args=[str(self.id)])
def __str__(self):
return self.attribute_set.name
When I try to create a new AttributeSetInstance with a Patient and AttributeSet argument I can access the patient and attribute_set fields, but not vice versa. Like so:
Python 2.7.13 (v2.7.13:a06454b1afa1, Dec 17 2016, 12:39:47)
[GCC 4.2.1 (Apple Inc. build 5666) (dot 3)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
(InteractiveConsole)
>>> from app.models import Patient, AttributeSet, AttributeSetInstance
>>> p = Patient(last_name='Doe', first_name='John', gender='M', dob='1973-07-16')
>>> p
<Patient: Doe, John (M, 1973-07-16)>
>>> a = AttributeSet(name='Set1')
>>> a
<AttributeSet: Set1>
>>> i = AttributeSetInstance(patient=p, attribute_set=a)
>>> i
<AttributeSetInstance: Set1>
>>> i.patient
<Patient: Doe, John (M, 1973-07-16)>
>>> i.attribute_set
<AttributeSet: Set1>
>>> p.attribute_set_instance_set
Traceback (most recent call last):
File "<console>", line 1, in <module>
AttributeError: 'Patient' object has no attribute 'attribute_set_instance_set'
>>>
So, i.patient works, but p.attribute_set_instance_set does not. As far as I could figure out, the default manager that is assigned to ForeignKey relationships appends a '_set' postfix to the backward relation.
Any idea what I'm doing wrong? It would be of great help.
It's probably something silly...
Ralph
You've added underscores where there shouldn't be any. The default related name is the lower case name of the model plus _set, so for AttributeSetInstance it is attributesetinstance_set.
If you want to use your underscore version, you can set it as the explicit related_name argument to the ForeignKey field.
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
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