Django Rest update many to many field of multiple objects at once - django

I'm working on chat app django rest backend. Btw I have a problem to update m2m field of multiple objects at once.
Inside the Message model there is an m2m field deleted which represents a list of users who deleted this message.
class Message(models.Model):
# other fields
deleted = models.ManyToManyField(User)
So I can implement the delete functionality by adding user inside that field when user deletes a specific message.
But the problem is when user deletes a conversation(all messages in it), how can I implement to update the delete field of multiple Message objects at once. Because each object has empty delete field, another user inside delete field, or same user inside delete field(means that user already deleted a message before).
Edit
According to validname's comment I'm adding an example to make clear about my problem.
Please imagine user1 and user2 has conversation1 and there are 3 messages inside that conversation. So currently message list of that conversation is like following:
[
{
id: 304,
conversation: 1,
...
deleted: []
},
{
id: 305,
conversation: 1,
...
deleted: [2] // user2 already deleted this message before
},
{
id: 306,
conversation: 1,
...
deleted: [1] // user1 already deleted this message before
},
]
And user2 just has deleted conversation1, so after that message list should be like following:
[
{
id: 304,
conversation: 1,
...
deleted: [2]
},
{
id: 305,
conversation: 1,
...
deleted: [2] // no need to change for existing user2
},
{
id: 306,
conversation: 1,
...
deleted: [1, 2] // insert user2
},
]
I just want a django function that works like above changes.

All m2m relations are implemented by extra table and have their own models which are accessible by field's thorough attribute.
So if you have a set of messages and you want to add user to 'deleted' for each of message, you can do it by bulk creating records for though model:
from itertools import cycle
message_qs = ... # Query set of message.
user = ... # User model instance.
# Construct list of [(message_1, user), (message_2, user), ...] etc.
data = list(zip(message_qs, cycle(user)))
# Transform data to through model instances to pass to bulk create.
through_model = Message.deleted.through
instances = [
through_model(message=m, user=u) for m, u in data
]
through_model.objects.bulk_create(instances)
This is supposed to be very efficient way. However if you don't care about efficiency at the moment, you can just iterate over each user and call message.deleted.add(user) for each.

Related

dynamodb table tables design for multi user sharing applications

Sorry for the abstract title of the question but i will try to explain my intention in details in my question.
I want to create a reminders application in which each user has a separate login in the system but he/she can choose to share an item(in this case a reminder) with another user if he/she chooses. So when that user with whom the item is shared searches in his app he can also see the reminders which are shared with him.
So a user can have a reminder for only himself + a reminder which is shared with him.
This are my data access/retrieval patterns:
So when a user goes inside the application he should be able to see a list of reminders that he created and also the ones which are shared with him
From that list he should be able to search for a reminder by tag(i plan to do that outside dynamodb since the tag would be a set and not a scalar field hence i cannot have an index on that) and also should be able to search for a reminder by title
3.A user should be able to update or delete a reminder
4.A user should be able to create a reminder
5.Also the user should only be able to see future reminders and not the ones in which the expiration date is passed
The table and index creation that i have is created using the below create_table script :
import boto3
def create_reminders_table():
"""Just create the reminders table."""
session = boto3.session.Session(profile_name='dynamo_local')
dynamodb = session.resource('dynamodb', endpoint_url="http://localhost:8000")
table = dynamodb.create_table(
TableName='Reminders',
KeySchema=[
{
'AttributeName': 'reminder_id',
'KeyType': 'HASH'
}
],
AttributeDefinitions=[
{
'AttributeName': 'reminder_id',
'AttributeType': 'S'
},
{
'AttributeName': 'user_id',
'AttributeType': 'S'
},
{
'AttributeName': 'reminder_title_reminder_id',
'AttributeType': 'S'
}
],
GlobalSecondaryIndexes=[
{
'IndexName': 'UserTitleReminderIdGsi',
'KeySchema': [
{
'AttributeName': 'user_id',
'KeyType': 'HASH'
},
{
'AttributeName': 'reminder_title_reminder_id',
'KeyType': 'RANGE'
}
],
'Projection': {
'ProjectionType': 'INCLUDE',
'NonKeyAttributes': [
'reminder_expiration_date_time'
]
}
}
],
BillingMode='PAY_PER_REQUEST'
)
return table
if __name__ == '__main__':
movie_table = create_reminders_table()
print("Table status:", movie_table.table_status)
So the decision for the global secondary index us to allow a user to search for reminders with a reminder title.
Now to achieve the above case in which a user wants to also share his reminder with someone else i want to do the below change to my table schema . Basically i want to rename the user_id attribute to something like users_id which initially contains the user id of the user who created it but if that reminder is shared with someone then the user_id of the second user is also concatenated with the creator user id and the users_id column is modified .
If i do this i have 2 issues which i can think of:
How do i know the user_id of the user with whom the reminder is shared ? May be now i need to maintain a new table holding user information ? Or can i use some other service like amazon cognito for this?
If i still have the Global Secondary index on the users_id column when i need to search for reminders for a user the query needs to be like : select * from reminders where users_id startswith("Bob")( for example) .
Another option which i can think of(preferred way) is to drop the idea of creating a users_id attribute but instead of keeping the user_id column as is . I would the add the user_id as a sort key (RANGE) key to the table so that the combination of reminder_id and user_id is unique. Then when a user wants to share his created reminder with some other user a new entry is created inside the database with the same reminder_id and a new user id (which is the user id of the user with whom the reminder is shared)
Any help on my dilemma would be greatly appreciated.
Thanks in advance.
You don't mention your query access pattern in any detail, and with DynamoDB your data model flows from the query access pattern. So the below is based only on my imagination of what query patterns you might need. I could be off.
The PK can be the user_id. The SK can be the reminder_id of all reminders the user keeps. That lets you do a Query to get all reminders for a given user. The primary key then is the user id and reminder id in combination, so if you're passing around a reference, use that (not just the reminder_id).
A share gets added by putting another item under the user_id of the person getting shared with. That way a Query for that user can retrieve both their own reminders and those shared with them.
If you need people to list what reminders they've shared and with others, you can put that into the reminder itself as a list of who it's been shared with, if the list is short enough, or instead create a GSI on that share reference (against a shared_by attribute) if the list might be large.
If you need to query for a user's reminders and differentiate their own vs shared, you can prepend the SK with that so SHARED#reminder_id or SELF#reminder_id so a begins_with on the SK can differentiate.
You can refine this in various ways, but I think it would optimize for the "show me my reminders and the reminders shared with me" use cases, while making sharing (or undoing sharing) easy to implement.

Can I restrict post method in flask restApi from adding another data to the database?

Given with my current data in db, I want to restrict the post method from adding another data to the database. What I want is restrict the post method in adding another data, and just update the existing data within the db.
Code:
def get(self):
predict = PredictModel.query.all()
return {'Predict': list(x.json() for x in predict)}
def post(self):
data = request.get_json()
new_predict = PredictModel(data['timelag1'],data['timelag2'],data['timelag3'],data['timelag4'],data['timelag5'])
db.session.add(new_predict)
db.session.commit()
db.session.flush()
return new_predict.json(),201
Current data in db:
"Predict": [
{
"timelag1":1,
"timelag2": 1,
"timelag3": 1,
"timelag4": 1,
"timelag5": 1
}
]
}
Data in db after a user entered another data:
"Predict": [
{
"timelag1":2,
"timelag2": 2,
"timelag3": 2,
"timelag4": 2,
"timelag5": 2
}
]
}
I recommend reading this answer concerning how to do the database manipulation (especially the later ones):
Flask-SQLalchemy update a row's information
you will need some kind of primary key or unique identifier to specify the row that you want to change - something like "id"
here's some sample code which will probably work if you adapt it to your case:
instance = User.query.filter(User.id==id)
data=instance.update(dict(json_data))
db.session.commit()
or
num_rows_updated = User.query.filter_by(username='admin').update(dict(email='my_new_email#example.com')))
db.session.commit()

How to fetch nested objects all at once using restFb?

So I can fetch a post using this code:
Post post = facebookClient.fetchObject("postId", Post.class, Parameter.with("id", "message","from"));
The "from" field returns the Author's id and name, but not as an User object.
To obtain the User Object I have to send a request again:
User user = facebookClient.fetchObject(post.getFrom().getId, User.class, ..);
This can lead to an insane amount of request calls.
Am I able to fetch them at once?
like this:
MyPost extends Post{
User fetchedUserWithFromFieldId;
}
and somehow invoke:
facebookClient.fetchObject("postId", MyPost.class, Parameter.with("id", "message","from")).deepFetch("fetchedUserWithFromFieldId",User.class, Parameter.with("link","name","location"..etc));
So basically I need the FB equivalent of SQL's join.
Here is the solution:
The parameter fields themselves can be nested.
source: https://restfb.com/documentation/ , Chapter: 'Request with fields (third to n-th level)'
facebookClient.fetchObject("postId", MyPost.class, Parameter.with("id", "message","from{link,name,location}"));
MyPost extends Post{
MyUser user; // MyUser has fields link,name,location.
}

Odoo error: return self.models[model_name] KeyError: 'res_groups_users_rel'

I need to make UI many2one dopdown list where I can identify users which depend to Manager group role.
Now I have dropdown field:
test = fields.Many2one('res.groups', 'Purchase request type', default=_get_users, track_visibility='onchange')
And I tried to write a function which can identify all users which depend to manager group role.
def _get_users(self):
pickings = self.env['res_groups_users_rel'].search([('gid','=',61)])
pickings_available = []
for picking in pickings:
pickings_available.append(picking)
return pickings_available
And I got an error:
return self.models[model_name]
KeyError: 'res_groups_users_rel'
I don't know how can I change this function and get value from amy2many relation.
I changed my function to:
def _get_users(self):
pickings = self.env['res.groups'].browse(61).users
pickings_available = []
for picking in pickings:
pickings_available.append(picking)
return pickings_available
and field:
test = fields.Many2one('res.users', 'Some text', default=_get_users, track_visibility='onchange')
I logged function _get_users and get values: [res.users(9,), res.users(65,)]
But I still can't get these values on my test field dropdown. What I am doing wrong?
If you are trying to get all users that belong to a group, why not do the following:
self.env['res_groups'].browse(61).users
On a side note, you might get an error, trying to assign a list as default value to a Many2one field.
Also you seem to be assigning users belonging to a group to a field that is specified to store reference to groups.
If you need to have a field to select a user that belongs to group with id 61, you can do the following:
test = fields.Many2one('res.users', 'Some description', domain="[('groups_id', 'in', [61])]")

Associating multiple items with a single REST call

I am using the Loopback framework, and have modelA which is in a many-to-many relation with modelB.
I want to know if it's possible to relate multiple items from modelB to modelA.
There is currently a way to relate one item with this call:
/modelA/{id}/modelB/rel/{fk}
Is there any way to perform this in a bulk operation?
If I understand your question correctly, I think you can simply do it through a filter. ModelA has many modelBs, Let me assume the relation name is 'modelBs'
modelA.find({
where: [your filter option on model A]
include: {
relation: 'modelBs',
scope: {
[your filters on model B]
}
}
})
in restful way:
/modelAs?filter[include]=modelBs&filter[where]....
The official documentation may help: https://docs.strongloop.com/display/public/LB/Querying+data
It seems to be HasManyThrough model.
I believe your model names starts with lowercase but ideally it should be ModelA and ModelB and their instances can then be saved in modelA and modelB variables.
In order to use add method, you will need to first find instance of ModelA first using ModelA.findById which you can save in modelA variable and then use the following code:
modelA.modelBs.add(modelBFieldsObject, function(err, patient) {
...
});
where modelBs should be the name of relation in the ModalA.json file as in
"relations": {
"modelBs": {
"type": "hasMany",
"model": "ModelB",
"foreignKey": "modelAId",
"through": "ModelAModelB",
"keyThrough": "modelBId"
}
...
I think it should be allowed to pass an array of modelBFieldsObject to create multiple instances as in
modelA.modelBs.add([modelBFieldsObject, modelBFieldsObject], function(err, patient) {
...
});
Tip: For clarity, name your models beginning with upper case letter and their instance variable in camelCase format.
References: Methods added to the model.