Querying many to many fields in django - python

In the models there is a many to many fields as,
from emp.models import Name
def info(request):
name = models.ManyToManyField(Name)
And in emp.models the schema is as
class Name(models.Model):
name = models.CharField(max_length=512)
def __unicode__(self):
return self.name
Now when i want to query a particular id say for ex:
info= info.objects.filter(id=a)
for i in info:
logging.debug(i.name) //gives an error
how should the query be to get the name
Thanks..

info.name is ManyToManyField so if you want all Name objects associated with it you have to use .all() method on it. Only then you'll get list (queryset) of Name objects:
info_list = info.objects.filter(id=a)
for info_object in info_list:
for name_object in info_object.name.all():
print name_object.name

Lukasz is right, but just so you know, it doesn't make sense to filter on an id unless you use info.object.filet(id__in=a) and a is a list of some sort. If you filter on a single id, you should be using objects.get(**kwargs) first of all, and it will return that specific Info instance instead of a QuerySet.

Related

Access Model Method for Multiple Objects in Django

Let's say I have created a model in Django that looks like this:
class Book(models.Model):
author = models.CharField(max_length=64)
... (other attributes)
def author_pretty(self):
return "[··" + self.author + "··]"
I need to get from all objects in DB the method author_pretty, as of now I'm doing this:
authors = Book.objects.values_list('author', flat=True)
authors_pretty = ["[·" + author + "·]" for author in authors]
# or
books = Book.objects.all()
authors_pretty = [book.author_pretty() for book in books]
Is there any way of doing this in a single call? Django would still have to make a query to DB and the call the method, but maybe there is a cleaner solution that I'm missing.
It depends on how you are going to use it, if it is one by one on a template, use the method as a property
#property
def author_pretty(self):
return "[··" + self.author + "··]"
However if you want something cleaner and for some reason you need them all in once, take a look into Django Docs for Concat, Query Expressions and Annotate
from django.db.models.functions import Concat
from django.db.models import Value, CharField
Book.objects.all().annotate(author_pretty=Concat(Value(“[··”), “author”, Value(“··]”), output_field=CharField()))
And then you can use it in a for loop like a property
for author in authors:
print(author.author_pretty)

How to print filtered results from django model

I am trying to print the filtered results from a django model. This is my code
record = StudentInfo.objects.filter(Name=name,
School=school,
City=city,
Country=country)
I know there are 4 entries that satisfy the filter. I now want to print the records. But when I try
print(record)
I get the following
[<StudentInfo: StudentInfo object (1)>, <StudentInfo: StudentInfo object (4)>, <StudentInfo: StudentInfo object (6)>, <StudentInfo: StudentInfo object (8)>]
How do I print the entire record as a list?
What you see in your model instance StudentInfo object (1) is the representation of your model instance. You can change it by overriding the str method on your model.
class StudentInfo(models.Model):
# fields
def __str__(self):
return self.Name
And it'll look like this.
[<StudentInfo: StudentName>, ..]
It's not a good approach to add all fields of your model to your str method.
If you want to see more info on the model instance, create another method on your model and use that.
class StudentInfo(models.Model):
# fields
def __str__(self):
return self.Name
def detail(self):
return f"Name: {self.name}, School: {self.school} ..."
Print your students like this.
for student in record:
print(student.detail())
Also, it's best practice to use all lowercase characters on your model fields. You may want to follow PEP-8 rules for more info.
As I understand you question, a student can have multiple records. So it is better to use SerializerMethodField.
On your student serializer, you can do something like
(considering you have serializer for StudentInfo)
class StudentSerializer(serializer.ModelSerializer):
record = serializer.SerializerMethodField()
class Meta:
model = Student
fields = "__all__"
def get_record(self, obj):
record = StudentInfo.objects.filter(Name=name,
School=school,
City=city,
Country=country)
return StudentInfoSerializer(record, many=True).data
after this, every time you use StudentSerializer, you will get the related record as a list.
when you use filter() method in django it returns all results that satisfies the criteria. The result returned by filter() is called Queryset.
[<StudentInfo: StudentInfo object (1)>, <StudentInfo: StudentInfo object (4)>, <StudentInfo: StudentInfo object (6)>, <StudentInfo: StudentInfo object (8)>]
This is a queryset.
Queryset is a list of objects.
To print the results:
for item in record:
print(item.Name)
print(item.Country)
print(item.School)
print(item.City)
for one iteration of the loop one object from the queryset is taken and we are accessing its properties using dot(.)
All the answers here require adding code to models, creating serializers, in case someone just needs to debug and see list of objects returned, I did it this way:
import json
from django.core.serializers.json import DjangoJSONEncoder
print(json.dumps(list(StudentInfo.objects.filter(Name=name, School=school, City=city, Country=country).values()), cls=DjangoJSONEncoder))
Yeah, Django doesn't make it easy, compared to Laravel, where it's just dd(StudentInfo.where(['Name' => name, 'School' => school, 'City' => city, 'Country' => country])); with a beautiful colored output.

How to update/delete embeded document in ListField based on its ID mongoengine?

I have the following documents:
class Note(EmbeddedDocument):
value = mongo_db.StringField(max_length=200, required=True)
id = mongo_db.UUIDField(required=True, primary_key=True)
class Post(Document,):
notes = mongo_db.ListField(mongo_db.EmbeddedDocumentField(Note))
How to write a statement that update the value field for a Note object inside the list of a Post object. in other words how to write something that do the following:
update(post_id, note_id, new_valye)
In similar way, how I can delete an instance of the embedded documents Note:
delete(post_id, note_id)
First I edited My Post Document to get the benefits of EmbeddedDocumentListField:
class Note(EmbeddedDocument):
value = mongo_db.StringField(max_length=200, required=True)
id = mongo_db.UUIDField(required=True, primary_key=True)
class Post(Document,):
notes = mongo_db.EmbeddedDocumentListField(Note, required=False)
Then used for updating:
Post.objects(
id=post_id,
notes__id=note_id
).update(
set__notes__S__value=my_value
)
And for delete:
Post.objects(id=post_id).update_one(
pull__notes__id=note_id
)
But I think, there is a problems with this approach, first update now is hard to write if you are updating many fields.
You've got 2 ways of doing this, let's assume you want to update the second item of the list (i.e at index=1):
1) Get your document instance and use .save()
post = Post.objects().first()
post.notes[1].value = 'new_value'
post.save()
2) If all you need is updating a field of a document, and you know its position in the array, then you can use:
Post.objects.update(set__notes__1__value='new_value')

'.....' object is not iterable in django

I have 3 models: User, Choice, Card. Each user will look at the same set of 10 cards and decides each one is important or not.
Here are how I define the classes and their relationship
In models.py:
class Choice(models.Model):
user = models.ForeignKey(User)
card = models.ManyToManyField(Card)
is_important = models.NullBooleanField()
class Card(models.Model):
card_number = models.IntegerField(primary_key=True)
content = models.TextField(null=False)
In views.py
(I try to save the choice for the card from the user. )
def listings(request):
user = request.user
choice = Choice.objects.create(user=user, is_important = True)
choice.card= Card.objects.get(1)
However, I got this error
'Card' object is not iterable
Could you please show me where the error is?
Many thanks!
You can add object against many to many field like this
card = Card.objects.create(card_number=any_number, content='abc')
choice.card.add(card)
First, it looks like you forgot pk= in your first .get() argument: Card.objects.get(pk=1)
Second, Choice.cards is a ManyToManyField that expects a list of items and not one in particular. You should set it through:
choice.card.set(Card.objects.filter(pk=1))
Please note that direct assignment with = will be deprecated from Django 1.10 and deleted in Django 2.0
.filter() will return a QuerySet (which is iterable). I think you wanted a ForeignKey instead of a M2M field, in which case your code would work (with the additional pk=).
In your function:
def listings(request):
user = request.user
choice = Choice.objects.create(user=user, is_important = True)
choice.card= Card.objects.get(1)
The following line is trying to fetch the Card object. However, we need to specify which card to be fetched.
If using an id, query it as:
choice.card= Card.objects.get(pk=1)
or else using list of ids:
choice.card = Card.objects.filter(pk__in=[12,22])
If using card_number field:
choice.card= Card.objects.get(card_number=1)
or else using list of card_numbers:
choice.card = Card.objects.filter(card_number__in=[12,22])

Django: exclude User list from all Users

I asked a similar question today (Django: exclude User list from all Users ), the sollution was to write a symmetrical query. Since I changed my models with a many-to-many relation, this solution isn't valid anymore.
How can I exclude a list of users from user instances? With this method:
class Shift(models.Model):
shift_location = models.CharField(max_length=200)
users = models.ManyToManyField(User)
def get_shift_users(self):
return self.users.all()
def get_other_users(self):
return User.objects.all().exclude(self.users.all())
I get the error: AttributeError: 'User' object has no attribute 'split'
You need to use the in operator:
User.objects.exclude(id__in=self.users.all())
By using an unevaluated queryset (self.users.all()), it is transformed in a subquery, so the result will be fetched in a single query.
Maybe something like this:
User.objects.all().exclude(id__in=[u.id for u in self.users.all()])

Categories

Resources