Foreign key get parent AND children in queryset - django

I have the following model:
class TopicModel(models.Model):
topic = models.ForeignKey('self', on_delete=models.CASCADE)
... some more not relevant fields...
I need to get a parent topic and its direct children in a queryset.
Currently I use it with this queryset:
TopicModel.objects.filter(
Q(pk=10) |
Q(topic__pk=10)
)
I was wondering whether I can do the same in a more simple query without using Q.

Related

Best way to serialize a boolean representing a reverse lookup

I have some models that look like the following, where I would like to serialize Parent with a boolean field representing whether an associated Child reverse lookup exists.
class Parent(models.Model):
... some fields
class Child(models.Model):
parent_fk = models.OneToOneField(
"Parent",
on_delete=models.CASCADE,
related_name="child",
)
... some other fields
class ParentSerializer(serializers.ModelSerializer):
has_child = serializers.BooleanField()
class Meta:
model = Parent
fields = ["has_child", ... some fields]
These are the solutions I've tried and what I understand to be the problems with them
just add "child" to fields - this inserts the value of the foreign key?
use SerializerMethodField - triggers a DB lookup for each row?
Is there a clean way to implement this feature?
You can annotate the Parent with an extra attribute:
from django.db.models import Exists, OuterRef
Parent.objects.annotate(
has_child=Exists(Child.objects.filter(parent_fk_id=OuterRef('pk')))
)
You can add this as queryset to the ViewSet, ListAPIView, etc.
The Parent objects that arise from this queryset will have an extra attribute .has_child.

How to validate and create related objects together with forms

I am trying to validate a related object (ForeignKey) when creating an object the base object with forms. The related object may or may not exist. Below I use MPTT but this is a general foreign key problem.
I have a model like this:
# model:
class MyMPTTModel(models.Model):
name = models.CharField(max_length=256, unique=True) # this is set
parent = TreeForeignKey('self', on_delete=models.CASCADE, null=True, blank=True, related_name='children')
#form
class MyMPTTModelForm(forms.ModelForm):
parent = mptt_forms.TreeNodeChoiceField(queryset=MyMPTTModel.objects.all())
class Meta:
model = MyMPTTModel
fields = ['name', 'parent']
I want to atomically get_or_create a set of nodes with the form(set?).
Something like:
paths = ['each-part/of-the-path/is-the-name', 'each-part/of-the-path/could-have-mutliple-children']
for path in paths:
parent = None
nodes = []
for p in path.split('/'):
nodes.append({'name': p, 'parent': parent })
parent = p
for node in nodes:
name, parent = node.values()
if parent:
parent = MyMPTTModel.objects.get_or_create(name=parent)[0]
MyMPTTModel.objects.get_or_create(name=name)
I'm struggling with the get_or_create part of the form as the parent may not exist and therefore is not a valid choice. I could create the parents before I create the next node, but then when it fails, it would create a bunch of orphan nodes since the children failed.
I want to validate each node and create them all together (or not).

Why Django ORM failing on simple foreign key relation?

I'm trying to simply create one to many relation model of categories using Django amazing ORM.
SQL:
create table categories(
id serial primary key not null,
parent_id int
);
insert into categories values(default,default,default);
update categories set parent_id = 1 where id > 1;
select * from categories;
id | parent_id
----+-----------
2 | 1
3 | 1
1 |
(3 rows)
Django amazing orm model:
class Categories(models.Model):
id = models.AutoField(primary_key=True)
parent_id = models.ForeignKey('self')
class Meta:
managed = False
db_table = 'categories'
Django Query:
Categories.objects.get(id=1)
OUTPUT:
django.db.utils.ProgrammingError: column categories.parent_id_id does not exist
LINE 1: SELECT "categories"."id", "categories"."parent_id_id" FROM "...
^
HINT: Perhaps you meant to reference the column "categories.parent_id".
Why it uses parent_id_id column instead of parent_id and how I can force it to use parent_id?
EDIT
I just changed parent_id field to parent.
EDIT 2
tatlar answer is not in my case becouse i already have database schema.
So after digging more deeper in documentation and other questions on stackoverflow there is what i have in result. This model contains reference to parent and children categories for each row. It could be inherited for all graph alike data models (comments, categories etc).
class Categories(models.Model):
id = models.AutoField(primary_key=True)
parent = models.ForeignKey('self', on_delete=None, parent_link=True, related_name='children')
class Meta:
managed = False
db_table = 'categories'
Get all children for category 1:
from myapp.models import Categories
ch = Categories.objects.get(id=1).children print (ch)
# <QuerySet [<Categories: Categories object (2)>, <Categories: Categories object (3)>]>
Get parent for category 2:
from myapp.models import Categories
ch = Categories.objects.get(id=1).parent
print (ch)
# <Categories: Categories object (1)>
Sorry to hear that you are having trouble with Django. In time you may grow to love the Django ORM and how it abstracts all the SQL code for you :)
You need to dig a little deeper into how the ORM works -- it's not a 1:1 replacement for SQL code. Check out the Model docs.
In your specific case, you need to create a new class called Parent and reference that class (via a ForeignKey) from your Categories class (you might also like to rename your Categories class to Category -- the ORM also handles plurals).
Try the code below (where I have already renamed Categories to Category for you):
from django.db import models
class Category(models.Model):
id = models.AutoField(primary_key=True)
parent = models.ForeignKey(Parent)
# ... Extra model attributes
class Meta:
verbose_name_plural = "categories"
class Parent(models.Model):
id = models.AutoField(primary_key=True)
# ... Extra model attributes
Then add all the extra attributes you need. This will create all the database tables, and their relationships, without you ever writing any SQL. If you are used to writing SQL it is a change, but it makes sense as you work more with the ORM and understand how good it is actually architected.
Good luck!
tatlar answer is not in my case becouse i already have database schema.
So after digging more deeper in documentation and other questions on stackoverflow there is what i have in result. This model contains reference to parent and children categories for each row. It could be inherited for all graph alike data models (comments, categories etc).
class Categories(models.Model):
id = models.AutoField(primary_key=True)
parent = models.ForeignKey('self', on_delete=None, parent_link=True, related_name='children')
class Meta:
managed = False
db_table = 'categories'
Get all children for category 1:
from myapp.models import Categories
ch = Categories.objects.get(id=1).children
print (ch)
# <QuerySet [<Categories: Categories object (2)>, <Categories: Categories object (3)>]>
Get parent for category 2:
from myapp.models import Categories
ch = Categories.objects.get(id=1).parent
print (ch)
# <Categories: Categories object (1)>

Django formset: show forms

I have the following models:
class DecompositionGoal(Model):
id = UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
parent = ForeignKey(Goal, related_name='related_src')
child = ForeignKey(Goal, related_name='related_dst')
In Goal model there is a boolean field is_quantitative.
For the given parent I want to change is_quantitative field for each child.
Don't know how to apply inlineformset for this.
What are the ways of accomplishing this task?

What lookup can I use for this issue?

I have the following Django model:
class MyModel(models.Model):
[...]
parent = models.ForeignKey('self', related_name="children", null=True, blank=True)
[...]
What I'm trying to do is count how many parents and grandparents a certain instance of the model has. That is, if instance_1 is the parent of instance_2, and instance_2 is the parent of instance_3, then instance_3 has a value of 2 (its parent, and the parent of its parent).
The trick is I'm trying to do this in a queryset as to do some filtering based on that value.
MyModel.objects.filter(??)
How can I go about doing that?
I thought about using annotate and Count and then filtering based on that, but again I'm not sure how to go about it.