Get Related Data from ManyToManyField in Django - python

Working with ManyToManyField I want to get data of all the users related to all the queried model object along with other field data in the model.
For example for the below model, I have 2 users related to this "ChatRoom"
class ChatRoomParticipants(models.Model):
user = models.ManyToManyField(User, related_name='chatroom_users')
room = models.ForeignKey(ChatRoom, on_delete=models.PROTECT)
With the below query
chatrooms = list(ChatRoomParticipants.objects.filter(user=user).values('user__user_uid', 'room__id', 'room__name'))
I'm able to fetch
[{'user__user_uid': UUID('f4253fbd-90d1-471f-b541-80813b51d610'), 'room__id': 4, 'room__name': 'f4253fbd-90d1-471f-b541-80813b51d610-872952bb-6c34-4e50-b6fd-7053dfa583de'}]
But I'm expecting something like
[{
'user__user_uid1': UUID('f4253fbd-90d1-471f-b541-80813b51d610'),
'user__user_uid2': UUID('872952bb-6c34-4e50-b6fd-7053dfa583de'),
'room__id': 4,
'room__name': 'f4253fbd-90d1-471f-b541-80813b51d610-872952bb-6c34-4e50-b6fd-7053dfa583de'
},
{
'user__user_uid1': UUID('f4253fbd-90d1-471f-b541-80813b51d610'),
'user__user_uid2': UUID('eecd66e7-4874-4b96-bde0-7dd37d0b83b3'),
'room__id': 5,
'room__name': 'f4253fbd-90d1-471f-b541-80813b51d610-eecd66e7-4874-4b96-bde0-7dd37d0b83b3'
},
{
'user__user_uid1': UUID('f4253fbd-90d1-471f-b541-80813b51d610'),
'user__user_uid2': UUID('4f4c0f3d-2292-4d06-afdc-1e95962ac5e6'),
'room__id': 6,
'room__name': 'f4253fbd-90d1-471f-b541-80813b51d610-4f4c0f3d-2292-4d06-afdc-1e95962ac5e6'
}]
I've searched and found I can do something like
user_data = chatrooms.users.all().values('user_uid')
But the above doesn't work well with filter and I would miss out data on room.
Note: I know that's not a correct method to do what I'm trying to achieve, if anyone can enlighten with what's the correct way to achieve the same data.

your example is somewhat confusing, but I think what you are looking for is to find the information of the users related to the same room.
chat_room = ChatRoomParticipants.objects.get(id=id_room)
users_chat = chat_room.user.all().values_list('user_uid', flat=True)
data = {
"room__id": chat_room.room.id
"room__name": chat_room.room.name
"users" : users_chat
}
for something more consistent you can use serializers

Related

Filter elements by optional lists of related elements

I have a feeling that I've made things more complex than they need to be - this can't possibly be such a rare case. It seems to me that it should be possible - or perhaps that I'm doing something fundamentally wrong.
The issue at hand is this: I've declared a database element, Element, which consists of about 10 many-to-many relations with other elements, one of which is Tag.
I want to enable the user of my application to filter Element by all of these relations, some of them or none of them. Say the user wants to see only Elements which are related to a certain Tag.
To make things even more difficult, the function that will carry out this objective is called from a graphql API, meaning it will recieve ID's instead of ORM objects.
I'm trying to build a resolver in my Python Flask project, using SQLAlchemy, which will provide an interface like so:
# graphql request
query getElements {
getElements(tags:[2, 3] people:[8, 13]) {
id
}
}
# graphql response
{
"data": {
"getElements": [
{
"id": "2"
},
{
"id": "3"
},
{
"id": "8"
}
]
}
}
I imagine the resolver would look something like this simplified pseudo-code, but I can't for the life of me figure out how to pull it off:
def get_elements(tags=None, people=None):
args = {'tags' : tags, 'people' : people}
if any(args):
data_elements = DataElement.query.filter_by(this in args) # this is the tricky bit - for each of DataElements related elements, I want to check if its ID is given in the corresponding argument
else:
data_elements = DataElement.query.all()
return data_elements
Here's a peek at the simplified database model, as requested. DataElement holds a lot of relations like this, and it works perfectly:
class DataElement(db.Model):
__tablename__ = 'DataElement'
id = db.Column(db.Integer, primary_key=True)
tags = db.relationship('Tag', secondary=DataElementTag, back_populates='data_elements')
class Tag(db.Model):
__tablename__ = 'Tag'
id = db.Column(db.Integer, primary_key=True)
data_elements = db.relationship('DataElement', secondary=DataElementTag, back_populates='tags')
DataElementTag = db.Table('DataElementTag',
db.Column('id', db.Integer, primary_key=True),
db.Column('data_element_id', db.Integer, db.ForeignKey('DataElement.id')),
db.Column('tag_id', db.Integer, db.ForeignKey('Tag.id'))
)
Please, ORM wizards and python freaks, I call upon thee!
I've solved it in a rather clunky manner. I suppose there must be a more elegant way to pull this off, and am still holding out for better answers.
I ended up looping over all the given arguments and using eval() (not on user input, don't worry) to get the corresponding database model. From there, I was able to grab the DataElement object with the many-to-many relationship. My final solutions looks like this:
args = {
'status' : status,
'person' : people,
'tag' : tags,
'event' : events,
'location' : locations,
'group' : groups,
'year' : year
} # dictionary for args for easier data handling
if any(args.values()):
final = [] # will contain elements matching criteria
for key, value in args.items():
if value:
model = eval(key.capitalize()) # get ORM model from dictionary key name (eval used on hardcoded string, hence safe)
for id in value:
filter_element = model.query.filter_by(id=id).one_or_none() # get the element in question from db
if filter_element:
elements = filter_element.data_elements # get data_elements linked to element in question
for element in elements:
if not element in final: # to avoid duplicates
final.append(element)
return final

deal with many2many field in odoo

I have two models skills and res_users.
I want that each user can have many skills and also each skill can have many users on it. I tried to do that but without success.
This is my skills:
class technicians_skills(osv.osv):
_name = 'skills'
_description = 'Technicians Skills'
_columns = {
'name': fields.char(string='name', size=50),
'description': fields.text(string="description"),
'member_ids': fields.many2many('res.users', 'skill', string='Technicians')
}
and this is the users :
class res_users(osv.osv):
_inherit = 'res.users'
_columns = {
'skill': fields.many2many('skills', relation='skills.rel', column1='name', column2='skill', string='Skill'),
}
and I want to know the skills of each user, but when I call this :
test = http.request.env['skills.rel'].search([])
It shows me this error
KeyError: 'skills.rel'
You need to specify all key words in your declaration to create the same relation tables
fields.many2many(
comodel_name='model.name',
relation='valid_postgres_name',
colum1='current_model_m2o_field_name',
column2='comodel_m2o_name',
string='Label')
And in the other definition keep the same name of the relation but inverse the other keyword.
In skills
user_ids = fields.many2many('re.users', 'user_skill_rel','user_id','skill_id', 'Users')
In users
skill_ids = fields.many2many('skills', 'user_skill_rel', 'skill_id', 'user_id', 'Skills')
See how i inversed the definition only the relation is the same.
Keep the same names of columns
EDITS:
don't execute search on relation because they are not models. you need to execute the search on the model then access the many2many fields.
Let say you want to get skill of the current user.
self.env.user.skill_ids # this will return the list of the skills for the current user
if you want to get the skills of more than one user.
result = self.env['res.users'].search([your_domain]])
# if you need to show user information then it's skill
for rec in result:
# use loop
rec.name # user name
rec.skill_ids
# but if you want to get the list of skill and you don't need users
skills = result.mapped('skills_ids') # mapped will return all the skills without duplication

Serialize OneToMany relation into array in Django

Suppose I have some django models:
class Restaurant(Model):
name = CharField(max_length=200)
class Food(Model):
restaurant = ForeignKey(Restaurant, on_delete=CASCADE)
name = CharField(max_length=200)
One Restaurant can have multiple foods.
And I want my json to look like this:
[
{
"name": "xxx",
"food": [{"name": "xxx"}, {"name": "xxx"}, ...]
}, ...
]
I can use something like:
restaurants = Restaurant.objects.all().values()
for restaurant in restaurants:
restaurant['food'] = Food.objects.filter(restaurant__id=restaurant['id']).values()
But this loses some optimization in SQL level, as this is using application level loop.
Is there a better way to do this?
check this
restaurant = Restaurant.objects.all()
result = []
for rest in restaurant:
data = {
'name': rest.name,
'food': rest.food_set.all().values('name')
}
result.appen(data)
Try it....
User django rest framework. using serialzers you can do it more effectively
#Nakamura You are correct, query inside the loop is not a good way of coding.
below is one way of solving
restaurants = Restaurant.objects.all().values()
foods = Food.objects.all()
for restaurant in restaurants:
restaurant['food'] = foods.filter(restaurant__id=restaurant['id']).values()
As you gave required JSON format i suggested this answer.

How to generate json data from a many to many database table?

I am using sqlite db and peewee as the ORM.
My data model is:
class User(UserMixin, db.Model):
nickname = CharField(index=True, unique=True)
class Circle(db.Model):
name = CharField(unique=True)
class UserInCircle(db.Model):
user = ForeignKeyField(User, related_name="in_circles")
circle = ForeignKeyField(Circle, related_name="include_users")
privilege = IntegerField()
What I need is to get a data format like the following:
[{"nickname": "urbainy", "privilege": 7, "in_circles": [{"circle_name": "world"}, {"circle_name": "test"}]}, {"nickname": "ywe", "privilege": 1, "in_circles": [{"circle_name": "family"}], {"nickname": "ymo", "privilege": null, "in_circles": []}]
So this is a nested json object. I tried marshmallow but I failed because of the many to many data structure. I can't get in_circles field all along time. I am beginner of programmer, so maybe this question is low level. But I really don't have idea to solve it. Thank you very much!
Now, I adopt this way to solve the problem:
#login_required
def setting():
users_in_circles = (User.select(User.nickname,
UserInCircle.privilege,
Circle.name.alias("circle_name"))
.join(UserInCircle, JOIN.LEFT_OUTER)
.join(Circle, JOIN.LEFT_OUTER)
.order_by(User.id))
users_in_circles_data = []
user_nickname = ""
user_in_circles = []
for user_in_circle in users_in_circles.naive():
if user_in_circle.nickname != user_nickname:
user_nickname = user_in_circle.nickname
user_in_circles = [dict(circle_name=str(user_in_circle.circle_name), privilege=str(user_in_circle.privilege))]
users_in_circles_data.append(dict(nickname=user_in_circle.nickname, in_circles=user_in_circles))
else:
user_in_circles.append(dict(circle_name=str(user_in_circle.circle_name), privilege=str(user_in_circle.privilege)))
users_in_circles_data[-1].update(nickname=user_in_circle.nickname, in_circles=user_in_circles)
print(users_in_circles_data)
return render_template("admin_setting.html", circles=Circle.select(), users=User.select(), users_in_circles_data=users_in_circles_data)
Somehow I think maybe there is some other neat way to implement this, such as marshmallow or some other tools. If you know a better solution, welcome to reply to my post then.

Django REST Framework - Relationships between nested object lists

I couldn't find an answer to this problem online. That said, I am new to building REST APIs and may be doing something fundamentally incorrect.
What I'd like to do is POST a JSON object with characteristics like this:
{
"groceryStoreVisit": 100,
"fruits": [
{"id":10, "type":"apple", "quantity":4},
{"id":20, "type":"orange", "quantity":3},
{"id":30, "type":"banana", "quantity":6}
],
"nuts": [
{"id":40, "type":"cashew", "no_bags":2},
{"id":50, "type":"peanut", "no_bags":4}
],
"bags": [
{"id":0, "type":"paper", "contents":[10,20,30]},
{"id":1, "type":"plastic", "contents":[40,50]}
]}
My issue is that, upon trying to serialize the data, I'm getting an error like this:
{"bags":[{"fruits":["Object with id=10 does not exist."]}]}
My Bag serialization class looks like this:
class BagSerializer(serializers.ModelSerializer):
contents = serializers.SlugRelatedField(many=True, queryset=Fruit.objects.all(), slug_field='id')
class Meta:
model = Bag
fields = ('id', 'type', 'contents')
My GroceryStoreVisit (entire object) serialization class looks like this:
class GroceryStoreVisitSerializer(serializers.ModelSerializer):
fruits = FruitSerializer(many=True)
bags = BagSerializer(many=True)
class Meta:
model = GroceryStoreVisit
fields = ('groceryStoreVisit', 'fruits', 'nuts', 'bags')
def create(self, validated_data):
fruits_data = validated_data.pop('fruits')
nuts_data = validated_data.pop('nuts')
bags_data = validated_data.pop('bags')
groceryStoreVisit = GroceryStoreVisit.objects.create(**validated_data)
for fruit_data in fruits_data:
Fruit.objects.create(groceryStoreVisit=groceryStoreVisit, **fruit_data)
for nut_data in nuts_data:
Nut.objects.create(groceryStoreVisit=groceryStoreVisit, **nut_data)
for bag_data in bags_data:
Bag.objects.create(groceryStoreVisit=groceryStoreVisit, **bag_data)
return groceryStoreVisit
I'm not sure if this is enough information to give the picture about what I'm trying to accomplish. There's nothing exotic about the models or Fruit/Nut serializers. Please let me know if there's anything I can clarify to make the problem definition more comprehensible.
I want to validate that the Bag contents exist within the top-level GroceryStoreVisit object. Can I do this as I'm trying to?
Thanks for the help!

Categories

Resources