django accessing raw many to many created table fields - django

Model:
class Subjects (models.Model):
name = models.CharField(max_length=100)
places = models.CharField(max_length=100)
class Student (models.Model):
name = models.CharField(max_length=40)
lastname = models.CharField(max_length=80)
subjects = models.ManyToManyField(Subjects, blank=True)
Django creates appname_student_subjects when I use model above.
appname_student_subjects table looks for example, like this:
id | student_id | subjects_id
-----------------------------------------
1 | 1 | 10
2 | 4 | 11
3 | 4 | 19
4 | 5 | 10
...
~1000
How can I access subjects_id field and count how many times subjects_id exists in the table above (and then do something with it). For example: If subject with id 10 exists two times the template displays 2. I know that I should use "len" with result but i don't know how to access subject_id field.
With foreign keys I'm doing it like this in a for loop:
results_all = Students.objects.filter(subject_id='10')
result = len(results_all)
and I pass result to the template and display it within a for loop but it's not a foreign key so it's not working.

You can access the through table directly.
num = (Students.subjects # M2M Manager
.through # subjects_students through table
.objects # through table manager
.filter(student_id=10) # your query against through table
.count())

Related

How to get count of subjects taken by each student?

I've two models 'Students' and 'Enrollments'.
The schema for these is as below:
class Students(models.Model):
id = models.AutoField(primary_key=True, unique=True)
name = models.CharField()
class Enrollments(models.Model):
enroll_id = models.AutoField(primary_key=True, unique=True)
student_id = models.ForeignKey(Students, on_delete=models.CASCADE)
subjects = models.charField()
I'm trying to achieve the result of following SQL query in Django Rest Framework, for getting number of subjects enrolled by students (individually).
select
s.id, s.name, count(e.subjects) as count
from Students as s
left outer join Enrollments as e
on e.student_id_id = s.id
group by s.id, s.name, e.subjects
order by count asc;
This query returns result like:
---------------------------
| id | name | count |
---------------------------
| 1 | a | 1 |
| 2 | b | 0 |
| 3 | c | 2 |
---------------------------
Can anyone please help me acheive this kind of result.
Note: I need 0 count students details also.
What you can do is when you are creating a serializer, you can add a serializer method field which will get the count for you.
Add this at the top of your serializer:
count = serializers.SerializerMethodField('get_count')
Then add a function inside your serializer like this:
def get_count(self, obj):
try:
return Enrollments.objects.filter(student_id=obj.id).count()
except:
return None
Finally, add 'count' to your field list. You can then add as many fields as you want. I hope this will get you your desired result. Also don't forget to use "select_related" in the ORM inside your view to reduce the amount of queries.

Django / postgres - maintain a table of counters

I want to maintain a running counter per user, that will be given to the user items.
User table will have a item_counter column
class Item:
name = models.CharField(max_length=100)
user = models.ForeignKey(User, on_delete=models.CASCADE)
item_num = models.IntegerField() # should be populated from the user table
The question, when the user is creating an item, what is the best way to increment the counter and to copy the new counter to the newly created item, atomically?
Technically I atomically need to:
Increment and get the counter of the user
Create a new Item with the value for item_counter
What is the django / postgres way to do that?
EDIT - example:
Users table:
| username | item_counter |
| john | 20 |
Now, the user sends POST /item. The result will be:
Items table:
| name | item_num |
| my_item | 21 |
Users table:
| username | item_counter |
| john | 21 |
Of course, there can be many users. Each user has an item_counter of its own.
From what I understand you need to assign a special integer to your newly created item record whose value will be fetched from a user's counter variable. The user's counter variable will increment every time the user creates a new Item record.
So as per your challenge this is your solution.
In your class Item declare a method for incrementing the value of counters
from django.db import transaction, IntegrityError #Django inbuilt atomicity transaction block
class Item(models.Model):
name = models.CharField(max_length=100)
user = models.ForeignKey(User, on_delete=models.CASCADE)
item_num = models.IntegerField()
def increment_counter(self):
temp = self #Here I have used temp so that it does not throw an out of reference error in your atomic block below
with transaction.atomic():
# This code executes inside a transaction.
temp.user.counter = temp.user.counter + 1
temp.user.save()
temp.item_num = temp.user.counter
temp.save()
In your views.py POST method once you saved/created the new Item you need to call the item.increment_counter() method
newly_created_item = Item.objects.create(....some data)
try:
newly_created_item.increment_counter()
except IntegrityError:
# Handle any exception here

Django reading data from 2 models with foreignkey and make single list

I'm new to django. I've been coding with sql but django orm is hard for me to convert my knowledge of sql to orm models.
I've client model
class client(models.Model):
c_id = models.AutoField(primary_key=True)
name= models.TextField()
age=models.IntegerField()
and address model
class address(models.Model):
c_id = models.ForeignKey(client, on_delete=models.CASCADE)
addr = models.CharField(max_lenght=20)
city= models.CharField(max_lenght=20)
This is my table
---------------------------
| c_id|Name | age |
---------------------------
| 1 | John | 23 |
----------------------------
| 2 | Rose | 20 |
----------------------------
------------------------------
| c_id|addr | city |
------------------------------
| 1 | buspark | florida|
------------------------------
| 2 | homesquare| florida|
------------------------------
how to get allclient with address in list
Look at values() docs
The values() method takes optional positional arguments, *fields,
which specify field names to which the SELECT should be limited. If
you specify the fields, each dictionary will contain only the field
keys/values for the fields you specify. If you don’t specify the
fields, each dictionary will contain a key and value for every field
in the database table.
__ allows get related data, so in your case it could look like this
address.objects.values('c_id__c_id', 'c_id__name', 'c_id__age', 'addr', 'city')

serializer.DictField does not save to database

I have this model:
class x(model.Models):
name = models.CharField(max_length=50, unique=True)
y = models.ManyToManyField(Y, related_name='y', db_table='x_y',
blank=False,null=False)
and this serializer:
class Serializer(DynamicFieldsModelSerializer):
class Meta:
model = models.x
fields = '__all__'
when I post data to this model I need to set this fields:
'name':['some name'],'y':['1','2']
this will make a row in database x with:
id | name
1 | some name
and two row in database x_y with:
id| x_id | y_id
1 | 1 | 1
2 | 1 | 2
the problem is that front end dose not send me 'name' and 'y' ,but send me 'name' and 'y[]', so in order to get data I needed to add this to my serializer class:
y= serializers.DictField(child=serializers.IntegerField(min_value=0, max_value=2))
but the result is that no data will save in x_y table.I dont know how to solve this
One issue that stands out to me is that you're using a DictField to handle a list of integers.
Presuming the integers are actually the primary key of your Y model, have you tried using y = PrimaryKeyRelatedField(many=True)? This seems like what you would be after.

Ordering by a OneToOne self-referencing field in Django

I'm trying to order a query by a self-referencing OneToOne field. The model:
class Foo(models.Model):
prior_foo = models.OneToOneField('self', related_name='following_foo', null=True, blank=True)
There is no guarantee that the pk for the linked item is higher or lower, (or any other field is useful besides the OneToOne reference) e.g.:
Foo.pk | prior_foo.pk
3 | null
6 | 3
5 | 6
10 | 5
I'd love to have the query return:
[foo_3, foo_6, foo_5, foo_10]