Reputation: 51
I have problem with creating object with recursive relation. So the scenario is right after create organization, insert user to just-created organization.
# models.py
class Organization(models.Model):
name = models.CharField(max_length=32)
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
code = models.CharField(max_length=4, unique=True)
photo_path = models.CharField(max_length=256, null=True)
class Meta:
db_table = 'organization'
def __str__(self):
return self.name
class OrganizationLevel(models.Model):
organization = models.ForeignKey(
Organization,
on_delete=models.CASCADE,
db_index=False
)
parent = models.ForeignKey(
'self',
on_delete=models.CASCADE,
db_index=False
)
name = models.CharField(max_length=48)
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
class Meta:
db_table = 'organization_level'
unique_together = ('name', 'organization')
class OrganizationUnit(models.Model):
organization_level = models.ForeignKey(
OrganizationLevel,
on_delete=models.CASCADE,
db_index=False
)
name = models.CharField(max_length=48)
position = models.PointField(geography=True, null=True)
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
parent = models.ForeignKey(
'self',
on_delete=models.CASCADE,
db_index=False
)
address = models.CharField(max_length=256)
class Meta:
db_table = 'organization_unit'
unique_together = ('name', 'organization_level')
class User(models.Model):
email = models.CharField(max_length=64)
username = models.CharField(max_length=32)
password = models.CharField(max_length=64)
token = models.CharField(max_length=32, null=True)
tokenexp = models.DateTimeField(null=True)
photo_path = models.CharField(max_length=256)
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
organization = models.ForeignKey(
Organization,
on_delete=models.CASCADE
)
is_activated = models.BooleanField(default=False)
code = models.CharField(max_length=32, null=True)
name = models.CharField(max_length=64)
birthdate = models.DateTimeField(null=True)
sex = models.CharField(max_length=1)
address = models.CharField(max_length=80)
organization_unit = models.ForeignKey(
OrganizationUnit,
on_delete=models.CASCADE
)
class Meta:
db_table = 'user'
So from given models, here's the flow:
I already try like this but got error
org = Organization.objects.create(
name=name,
code=code.upper()
)
org.save()
lvl = OrganizationLevel.objects.create(
organization=org,
parent=org.id,
name="Level1"
)
lvl.save()
unit = OrganizationUnit.objects.create(
name="Unit Name",
organization_level=lvl,
parent=lvl.id
)
unit.save()
Cannot assign "6": "OrganizationLevel.parent" must be a "OrganizationLevel" instance.
And what's the right answer?
Upvotes: 1
Views: 463
Reputation: 77952
For a recursive relationship (ForeignKey to self) the foreign key needs to accept null - else you will never be able to create at least the first instance (else it would need a reference to another pre-existing record, which cannot be created without a reference to yet another pre-existing record etc - chicken & egg problem...), so you want:
class OrganizationLevel(models.Model):
# ...
parent = models.ForeignKey(
'self',
on_delete=models.CASCADE,
db_index=False,
null=True,
blank=True
)
and
class OrganizationUnit(models.Model):
# ...
parent = models.ForeignKey(
'self',
on_delete=models.CASCADE,
db_index=False,
null=True,
blank=True
)
And when creating the first Level and Unit, leave those blanks:
lvl = OrganizationLevel.objects.create(
organization=org,
parent=None,
name="Level1"
)
unit = OrganizationUnit.objects.create(
name="Unit Name",
organization_level=lvl,
)
Note that YourModel.objects.create()
does create the record in the database so you don't need to call .save()
on your instances here.
Then when you want to add a child level or child unit you have to pass either the parent instance (not it's id - the instance itself) as parent
argument OR pass the parent instance id as parent_id
argument (same for any ForeignKey actually: either your pass 'fieldname=related_instance' or 'fieldname_id=related_instance_id).
Upvotes: 2
Reputation: 11
what if you change your Organitation level models:
class OrganizationLevel(models.Model):
organization = models.ForeignKey(
Organization,
on_delete=models.CASCADE,
db_index=False
)
parent = models.ForeignKey(
'self',
on_delete=models.CASCADE,
db_index=False,
blank=True,
null=True
)
name = models.CharField(max_length=48)
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
class Meta:
db_table = 'organization_level'
unique_together = ('name', 'organization')
Upvotes: 1