Django: Accessing ForeignKey fields and creating new relations based on them - django

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.

Related

How to relate two tables with one of the field equal?

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)

Django: Problem with saving model instances with ForeignKey-relation to database

I would like to create and save model instances with Django.
These are my models:
class Customer(models.Model):
id = models.IntegerField(primary_key=True, unique=True)
user = models.OneToOneField(User, on_delete=models.CASCADE)
first_name = models.CharField(max_length=255)
last_name = models.CharField(max_length=255)
...
class Order(models.Model):
id = models.IntegerField(primary_key=True, unique=True)
customer = models.ForeignKey(Customer, on_delete=models.CASCADE)
comment = models.TextField()
...
I create an order like this:
def store_storage_space_order(cleaned_data):
try:
user = User.objects.create_user(
cleaned_data["customer_email"],
cleaned_data["customer_email"],
1234
)
customer = Customer.objects.create(
user=user,
first_name=cleaned_data["customer_firstname"],
last_name=cleaned_data["customer_lastname"],
email=cleaned_data["customer_email"],
phone_number=cleaned_data["customer_phone"]
)
StorageSpaceOrder.objects.create(
customer=customer,
order_price=Decimal(cleaned_data["order_price"])
)
except Exception as exc:
logger.exception("Couldn't store any order information", exc, cleaned_data)
As far as I've learned, Django will save the object on calling create as well.
Trying to save the order, I get the following error message:
save() prohibited to prevent data loss due to unsaved related customer
What I don't get; customer is already there and saved in the database. There are no changes on the customer in between.
Also, I've tried customer_id=customer.id/pk, - but both ID and PK return None.
Why is this and what do I need to change? Loading the object again is not the preferred way, as I only got the ID which marks it as unique.
Thanks for your input :)
ID fields have to be auto-generated by Django, so you don't need to declare them in your models.
If you do, you override the default behaviour called when an instance of this model is created/saved.

How do i created multiple connected objects at once in Django Rest Framework?

I have following model in my Django Model
class Match(models.Model):
initiator = models.ForeignKey('CustomUser', related_name='match_initiator', on_delete=models.CASCADE)
matched_person = models.ForeignKey('CustomUser', related_name='matched_person', on_delete=models.CASCADE)
created_at = models.DateTimeField(auto_now=True)
algo_version = models.IntegerField()
room_no = models.ForeignKey('Room', related_name='room_match', on_delete=models.CASCADE, null=True, blank=True)
class Room(models.Model):
name = models.CharField(max_length=20, unique=True)
members = models.ManyToManyField(CustomUser, related_name="joined_rooms")
created_at = models.DateTimeField(auto_now=True,editable=False)
is_active = models.BooleanField(default=True)
So basically I have two models, Room and Match with Match having a foreign key to a room. Now I want to create both of these objects at once, but I can not do it as first I have to create Room object get the id of it and assign it to Match object instance and then save it again. Problem with this is that if any of the model save is failed, it breaks the code.
Is there any way I can create both Match and Room object at once?
I have found this reference in Django Documentation: But I am not sure whether Django handle it automatically or do I need to handle it.
Thanks in advance!!!
You can use atomic transactions to save both objects or none if something fails.
from django.db import IntegrityError, transaction
def your_function(...):
try:
with transaction.atomic():
room = Room.objects.create(...)
match = Match.objects.create(room_no=room, ...)
....
except IntegrityError:
handle_exception()
If something inside the transaction fails nothing will be saved.
You'll need to handle it yourself if your serializers are nested.
What it may look like is the following. This is entirely a guess since I don't have existing serializers to work off of, but I hope it'll help you understand the goal.
class MatchSerializer(serializers.ModelSerializer):
room = RoomSerializer()
class Meta:
model = Match
fields = (...)
def create(self, validated_data):
room_data = validated_data.pop('room')
room = Room.objects.create(**profile_data)
validated_data['room'] = room
return super().create(self, validated_data)
You'll also need to handle the update method which was also mentioned in the documentation you linked to.
You can simply use:
try:
room = Room.objects.create(...)
Match.objects.create(room=room, ...)
except:
print('Catch exception here...')

Django ModelForm: Huge ForeignKey queryset leads to crash when loading form

Basically, I have two models: User and Event. An event is always associated with one user.
class User(models.Model):
user_id = models.AutoField(primary_key=True)
username = models.CharField(max_length=255, unique=True)
hashed_password = models.CharField(max_length=255)
class Event(models.Model):
event_id = models.AutoField(primary_key=True)
title = models.CharField(max_length=255)
description = models.TextField(max_length=255, blank=True, default='')
user = models.ForeignKey(User)
And then I have the following form for Event.
class EventForm(forms.ModelForm):
class Meta:
model = Event
fields = ['title', 'description', 'user']
I can succesfully show this form in my template to create an event. I can also associate a user to a form successfully with Select field when the users number are still few.
Now the problem is, when I have 1M users in database, my browser crashes when loading the template. Any idea how to solve this one? I was thinking about using AJAX and then search user that matches the username, but I'd like to hear other better approaches. Thanks!

Django filtering time

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)