I have a model called Message and this has a field called in_response_to . I use this field to identify which messages are related to each other by storing the primary key of the original message inside each related message and I list all the messages via time created.
So for example I created 4 message and each of time are related to each other and shows the created time
Hello 1.00am
My name is richard 2.00am
What yours? 3.00am
Kangarro 4.00am
and I retrieve object with the message What yours? 3.00am . How can I show all the messages that are related to this object eariler than the created time?
class Message(models.Model):
user = models.ForeignKey(User, related_name='sender')
recipient = models.ForeignKey(User, related_name='recipient')
created = models.DateTimeField(auto_now_add=True)
in_response_to = models.ForeignKey('self', null=True, blank=True)
def __unicode__(self):
return self.body
views
messages = Message.objects.get(pk=id,recipient=request.user.id)
message = Message.objects.filter(in_response_to=messages.in_response_to )
I think you inverted messages with message. Assuming message is your single object, we'll filter messages like so:
messages = Message.objects.filter(in_response_to=message.in_response_to).filter(created__lt=message.created)
Check the docs for more comparison examples (lte means less than or equal but you could use lt, gt, gte and so on)
About the DateTime thing:
created = models.DateTimeField(default=datetime.datetime.now)
is one of the options you have (you have to import datetime). The other one is overriding your model's save method:
class Message(models.Model):
# other fields omitted to keep it clean
created = models.DateTimeField(blank=True, null=True)
def save(self, *args, **kwargs):
if not self.created:
self.created = datetime.datetime.now()
return super(Message, self).save(*args, **kwargs)
Related
I am new with django and django rest framework. Excuse me for my bad english...
I am trying to relate two django models:
First one (class ModulMess) receives messages from remote numbered modules
-moduleA received message "AAA".
-moduleB received message "BBB"
-moduleA received message "CCC"
-moduleC received message "DDD"
Second one (class Owner) is a list of users who own the modules
-UserXX owns moduleA and moduleC
-UserYY owns moduleB
I am tryng to make search filter in order to list this sort of message for actual user:
For example, for UserXX:
UserXX received from moduleA messages "AAA" and "CCC"
and from moduleC message "DDD"
Please could you explain me theorically how to manage it? I am not abble to visualize how to make relations between the two models, as they have one equal field each one...
Thank you very much!
I tried:
class Owner(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE,related_name='usermod')
modulkey = models.CharField(max_length=255)
date_in = models.DateTimeField(default=timezone.now, blank=True)
def __str__(self):
return f'{self.user.first_name} {self.user.last_name} | {self.modulkey}'
class ModulMess(models.Model):
modulkey = models.ForeignKey(Owner, on_delete=models.CASCADE)
date_ini = models.DateTimeField(default=timezone.now, blank=True)
message = models.CharField(max_length=255)
But cannot reach to achieve a good serializer nor view...
I think your modelling is correct. You also need the correct queryset logic and you are set to go. But I'll try to provide the viewset and serializer.
Though the FK modulkey in ModuleMess model can be changed to owner as it makes more sense:
class ModulMess(models.Model):
owner = models.ForeignKey(Owner, on_delete=models.CASCADE)
date_ini = models.DateTimeField(default=timezone.now, blank=True)
message = models.CharField(max_length=255)
The query logic can be defined in a different function like below:
def get_messages_for_user(user):
# Get the owner object for the user
owner = Owner.objects.get(user=user)
# Filter the ModulMess queryset to include only messages for the user's modulkey
messages = ModulMess.objects.filter(owner__modulkey=owner.modulkey)
return messages
Then for serializer we can use model serializer class. Like below:
class ModulMessSerializer(serializers.ModelSerializer):
class Meta:
model = ModulMess
fields = ['id', 'date_ini', 'message', 'owner']
And lastly the viewset will look like below:
class ModulMessViewSet(viewsets.ViewSet):
permission_classes = [IsAuthenticated]
serializer_class = ModulMessSerializer
def list(self, request):
user = request.user
messages = get_messages_for_user(user)
serializer = self.serializer_class(messages, many=True)
return Response(serializer.data)
I have two models
Project Model
class Project(models.Model):
name = models.CharField(max_length=200)
workflow = models.ForeignKey("WorkflowType", null=True, blank=True, on_delete=models.SET_NULL)
created_on = models.DateTimeField(auto_now_add=True)
id = models.UUIDField(default=uuid.uuid4, editable=False, unique=True, primary_key=True)
def __str__(self):
return self.name
Workflow Instance Model
class WorkflowInstance(models.Model):
workflow_step = models.ForeignKey('WorkflowStep', null=True, blank=True, on_delete=models.CASCADE)
project = models.ForeignKey('Project', null=True, blank=True, on_delete=models.SET_NULL)
I want to check if the value of workflow field in "Project" models is added or changed for a particular project.
I am approaching the problem in following manner:
Checking if the previous and the new value of the "workflow" field in a project are different. If yes (modifies), then create the new instance of a project.
#receiver(pre_save, sender=Project)
def projectToBeUpdated(sender, instance, **kwargs):
if instance.id is None:
pass
else:
previous = Project.objects.get(id=instance.id)
if previous.workflow != instance.workflow:
print("workflow value modified. Please create a WorkflowInstance")
Problem: The comparison for previous and new value of the "workflow" field are happening in "pre_save" signal. But my new instance creation for workflowInstance is to be created in "post_save" signal. How can I do this?
Also, ideally I would like to store the previous value of workflow field in "pre_save" and get the new value of the field in "post_save". Reason being, save() method might fail for any reason, while I am comparing the previous and new value in "pre_save" method itself. Making changes in the database without confirming if the save() method executed successfully would be a wrong approach in my view.
You can override the model save method itself and use the following code logic, additionally if you want to identify what fields got modified you can use the dirtyfields package as well.
def save(self, *args, **kwargs):
if not self._state.adding:
changed_attr = self.get_dirty_fields()
else:
is_new = True
super().save(*args, **kwargs)```
I'm currently making a Todo app, I have multiple Todolist that can each contain multiple tasks, here's how I made my models :
class Todo(models.Model):
name = models.CharField(max_length=120)
total_tasks = models.IntegerField(default=0)
completed_tasks = models.IntegerField(default=0)
def update_total_tasks(self):
self.total_tasks = self.task_set.all()
def _str_(self):
return self.name
class Task(models.Model):
name = models.CharField(max_length=120)
completed = models.BooleanField(default=False)
todo = models.ForeignKey(Todo, on_delete=models.CASCADE, related_name="tasks")
My todo contains all current tasks as well as completed tasks, the update_total_tasks function is meant to query all tasks linked to that particular todo and update the field accordingly.
This function is called each time a task is created / updated with :
#receiver(models.signals.post_save, sender=Task)
def execute_after_save(sender, instance, created, *args, **kwargs):
instance.todo.update_total_tasks()
The receiver works and calls my update function properly though it seems the query is done the wrong way because I get this error :
AttributeError: 'Todo' object has no attribute 'task_set'
Do you have any idea on why it's not working properly ?
Thanks.
The related_name=… parameter [Django-doc] is set to 'tasks'. Hence you access the relation in reverse with .tasks.
You thus access this with:
class Todo(models.Model):
name = models.CharField(max_length=120)
total_tasks = models.IntegerField(default=0)
completed_tasks = models.IntegerField(default=0)
def update_total_tasks(self):
self.total_tasks = self.tasks.count()
I'm fairly new to Django I'm trying to build a multichat app using Django 2.0.5 and my models are the following:
class DateTimeModel(models.Model):
date_created = models.DateTimeField(auto_now_add=True)
date_modified = models.DateTimeField(auto_now=True)
class Meta:
abstract = True
class Room(DateTimeModel):
id = models.UUIDField(primary_key=True, default=uuid.uuid4,
editable=False
)
members = models.ManyToManyField(User)
def __str__(self):
...
class Message(DateTimeModel):
sender = models.ForeignKey(User, on_delete=models.CASCADE)
room = models.ForeignKey(Room, on_delete=models.CASCADE)
text = models.TextField()
def __str__(self):
...
What I want to do: I want to set the database scheme such that each message knows who its recipients are in the room that it belongs to and somehow stores whether each member in that room has 'seen' the message. I am having a tough time trying to see how I could make it work.
Thanks in advance!
Ahmed
To get message recipients, use (for example if we have a message in the database, with id=1)
message = Message.objects.get(pk=1)
recipients = message.room.members
For the part where you said you would want to see who has seen the message. Use the following method(it's not the only way, but an example):
Add another field to your Message model which would be ManyToMany field.
Maybe like seen = models.ManyToManyField(User) Such that when you catch the event, maybe when a user clicks on the message, send a post/get request to the server, notifying it of a seen event and there you use the following code:
message = Message.objects.get(id=POST_DATA['message_id'])
user = request.user
if user not in message.seen:
message.seen.add(user)
'''
continue with app logic
'''
I hope you got an idea.
I have a Django model and I want to modify the object permissions on or just after save. I have tried a few solutions and the post_save signal seemed the best candidate for what I want to do:
class Project(models.Model):
title = models.CharField(max_length=755, default='default')
assigned_to = models.ManyToManyField(
User, default=None, blank=True, null=True
)
created_by = models.ForeignKey(
User,
related_name="%(app_label)s_%(class)s_related"
)
#receiver(post_save, sender=Project)
def assign_project_perms(sender, instance, **kwargs):
print("instance title: "+str(instance.title))
print("instance assigned_to: "+str(instance.assigned_to.all()))
In this case, when a Project is created, the signal fires and I see the title, but an empty list for the assigned_to field.
How can I access the saved assigned_to data following save?
You're not going to. M2Ms are saved after instances are saved and thus there won't be any record at all of the m2m updates. Further issues (even if you solve that) are that you're still in a transaction and querying the DB won't get you m2m with proper states anyways.
The solution is to hook into the m2m_changed signal instead of post_save.
https://docs.djangoproject.com/en/dev/ref/signals/#m2m-changed
Your sender then would be Project.assigned_to.through
If your m2m can be empty (blank=True) you are in a little trouble with m2m_changed, because m2m_changed doesn't fire if m2m wasn't set. You can solve this issue by using post_save and m2m_changed at the same time. But there is one big disadvantage with this method - your code will be executed twice if m2m field isn't empty.
So, you can use transaction's on_commit (Django 1.9+)
Django provides the on_commit() function to register callback
functions that should be executed after a transaction is successfully
committed.
from django.db import transaction
def on_transaction_commit(func):
def inner(*args, **kwargs):
transaction.on_commit(lambda: func(*args, **kwargs))
return inner
#receiver(post_save, sender=SomeModel)
#on_transaction_commit
def my_ultimate_func(sender, **kwargs):
# Do things here
Important note: this approach works only if your code calls save().
post_save signal doesn't fire at all in cases when you call only instance.m2m.add() or instance.m2m.set().
Use transaction on commit!
from django.db import transaction
#receiver(post_save, sender=Project)
def assign_project_perms(sender, instance, **kwargs):
transaction.on_commit(lambda: print("instance assigned_to: "+str(instance.assigned_to.all())))
here is an example about how to use signal with many to many field (post like and post comments models),
and in my example i have :
like model (Intermediary table for User and Post tables) : the user can add 1 record only in Intermediary table for each post , which means (unique_together = ['user_like', 'post_like']) for this type of many to many relations you can use 'm2m_changed' signals ,
comment model (Intermediary table for User and Post tables): the user can add many records in Intermediary table for each post , (without unique_together ), for this i just use 'post_save, post_delete' signals , but you can use also 'pre_save, pre_delete' if you like ,
and here is both usage example :
from django.db import models
from django.contrib.auth.models import User
from django.db.models.signals import post_save, post_delete, m2m_changed
from django.dispatch import receiver
class Post(models.Model):
post_user = models.ForeignKey(User,related_name='post_user_related', on_delete=models.CASCADE)
post_title = models.CharField(max_length=100)
post_description = models.TextField()
post_image = models.ImageField(upload_to='post_dir', null=True, blank=True)
post_created_date = models.DateTimeField(auto_now_add=True)
post_updated_date = models.DateTimeField(auto_now=True)
post_comments = models.ManyToManyField(
User,
through="Comments",
related_name="post_comments"
)
p_like = models.ManyToManyField(
User, blank=True,
through="LikeIntermediary",
related_name="post_like_rel"
)
class LikeIntermediary(models.Model):
user_like = models.ForeignKey(User ,related_name="related_user_like", on_delete=models.CASCADE)
post_like = models.ForeignKey(Post ,related_name="related_post_like", on_delete=models.CASCADE)
created = models.DateTimeField(auto_now_add=True)
def __str__(self):
return f"{self.user_like} - {self.post_like} "
class Meta:
unique_together = ['user_like', 'post_like']
#receiver(m2m_changed, sender=LikeIntermediary)
def like_updated_channels(sender, instance, **kwargs):
print('this m2m_changed receiver is called, the instance is post id', instance.id)
class Comments(models.Model):
cmt_user = models.ForeignKey(User, on_delete=models.CASCADE, related_name="related_comments_user")
cmt_post = models.ForeignKey(Post, on_delete=models.CASCADE, related_name="related_comments_post")
cmt_created_date = models.DateTimeField(auto_now_add=True)
cmt_comment_body = models.TextField()
cmt_created = models.DateTimeField(auto_now_add=True)
cmt_updated = models.DateTimeField(auto_now=True)
#receiver(post_save, sender=Comments)
def comments_updated_channels(sender, instance, created, **kwargs):
print('this post_save receiver is called, the instance post id', instance.cmt_post.id)
#receiver(post_delete, sender=Comments)
def comments_deleted_channels(sender, instance, **kwargs):
print('this post_save receiver is called, the instance post id', instance.cmt_post.id)
notes :
the instance with 'm2m_changed' it is a post object .
the instance with 'post_save and post_delete' it is a comment object
this is just an example , and change it based on your case/requirements.
i hope this helpful