I need to Prefetch data but only last in django - django

class AppliedEvent(models.Model):
event_type = models.ForeignKey(EventType, on_delete=models.CASCADE, related_name='applied_events', null=True)
seedbed = models.ForeignKey(Seedbed, on_delete=models.CASCADE, null=True, related_name='applied_events')
seed = models.ForeignKey(Seed, on_delete=models.CASCADE, related_name='applied_events', null=True)
user = models.ForeignKey(UserModel, on_delete=models.CASCADE, related_name='applied_events', null=True)
class AppliedEventTime(models.Model):
start_time = models.DateTimeField()
end_time = models.DateTimeField()
event = models.ForeignKey(AppliedEvent, on_delete=models.CASCADE, related_name='times')
applied_by = models.ForeignKey(UserModel, on_delete=models.CASCADE, related_name='applied_times', null=True)
User have ability to add new event time by creating new AppliedEventTime instance.
class AppliedEventMixin:
request: Any
def get_queryset(self) -> QuerySet:
return AppliedEvent.objects.filter(user_id=self.context.request.user).select_related(
'user', 'event_type', 'seedbed', 'seed'
).only('user__username', 'event_type__name', 'seedbed__id', 'seed__id').prefetch_related(
'times'
)
This is my query set mixin for django-extra controller
#api_controller('/applied_event', auth=JWTAuth(), permissions=[IsAuthenticated])
class AppliedEventController(AppliedEventMixin):
#route.get('/applied_event/{event_id}', tags=['applied_event'], response=AppliedEventOutSchema)
def event_by_id(self, event_id: int):
event = get_object_or_404(self.get_queryset(), pk=event_id)
return event
This is controller for API and point.
class EventTypeNameSchema(Schema):
id: int
name: str
AppliedEventTimeSchema = create_schema(AppliedEventTime, exclude=['id', 'event'], custom_fields=[('applied_by', UserOutSchema, None)])
class UserOutSchema(Schema):
id: int
username: str
class AppliedEventOutSchema(ModelSchema):
event_type: EventTypeNameSchema
user: UserOutSchema
times: List[AppliedEventTimeSchema]
class Config:
model = AppliedEvent
include = ['id', 'seedbed', 'seed']
This is my schema.
I am trying to prefetch only the latest added AppliedEventTime instance related to AppliedEvent in my Django project. I have a AppliedEvent model that has a foreign key to AppliedEventTime model and a user foreign key, the AppliedEventTime model has a foreign key to the AppliedEvent model and a user foreign key.
my question is, how can i prefetch only latest added AppliedEventTime instance related to AppliedEvent?

you can use order_by('-id') to order the AppliedEventTime instances in descending order and use first() method to get the latest added instance.
class AppliedEventMixin:
request: Any
def get_queryset(self) -> QuerySet:
return AppliedEvent.objects.filter(user_id=self.context.request.user).select_related(
'user', 'event_type', 'seedbed', 'seed'
).only('user__username', 'event_type__name', 'seedbed__id', 'seed__id').prefetch_related(
Prefetch('times', queryset=AppliedEventTime.objects.order_by('-id').first())
)
or, alternatively what you can do is modify the class AppliedEventMixin to prefetch only the latest added AppliedEventTime instance related to AppliedEvent. Replace the line with Prefetch... in the above solution i provided with:
Prefetch('times', queryset=AppliedEventTime.objects.latest())

Related

Properly update Django model's foreign key property

I have a Django Model named EmailSendingTask. This is the whole model-
class EmailSendingTask(models.Model):
order = models.OneToOneField(Order, on_delete=models.CASCADE, related_name='order')
status = EnumChoiceField(SetupStatus, default=SetupStatus.active)
time_interval = EnumChoiceField(TimeInterval, default=TimeInterval.five_mins)
immediate_email = models.OneToOneField(PeriodicTask, on_delete=models.CASCADE, null=True, blank=True, related_name='immediate_email')
scheduled_email = models.OneToOneField(PeriodicTask, on_delete=models.CASCADE, null=True, blank=True, related_name='scheduled_email')
created_at = models.DateTimeField(auto_now_add=True)
class Meta:
verbose_name = 'EmailSendingTask'
verbose_name_plural = 'EmailSendingTasks'
def __str__(self) -> str:
return f'EmailSendingTask: Order = {self.order}'
The immediate_email and scheduled_email fields are responsible for holding two PeriodicTask objects.
I have created a function called disable_scheduled_email, which is responsible for disabling the scheduled_email's periodic task. The detail of the function is here-
def disable_scheduled_email(self):
print(f'Disabling scheduled email...')
self.scheduled_email.enabled = False
self.save(update_fields=['scheduled_email'])
Now, whenever I call this function and print the value of the self.scheduled_email.enabled, I find it False. But, when I try to look at the Django Admin site, the periodic task's enabled value remains as True. Why is it happening?
After some experiments into the Django Shell I have found out that, I was not specifically calling save() to the foreign key (scheduled_email). I have just added self.scheduled_email.save() into the disable_scheduled_email function. So, the whole function became like:
def disable_scheduled_email(self):
print(f'Disabling scheduled email...')
self.scheduled_email.enabled = False
# self.save(update_fields=['scheduled_email'])
self.scheduled_email.save() #instead of self.save(...), wrote this

Can I use any method to choose a specific foreign key like I used while using get in Django?

Can I use any method to choose a specific foreign key like I used while using get?
session = StudentReporter.objects.get(pk=pk).session_report.session.pk
session_report = StudentReporter.objects.get(pk=pk).session_report.pk
studentsession = StudentSessions.objects.filter(session=session).select_related('student', 'session', 'session__teacher', 'session__name', 'session__day')
# I tried this for loop but it returns a string not a model instance
student = tuple([(student.student,student.student) for student in studentsession])
self.fields['student'] = forms.ChoiceField(choices= student)
I want the code to be like that
session = StudentReporter.objects.get(pk=pk).session_report.session.pk
session_report = StudentReporter.objects.get(pk=pk).session_report.pk
# watch the change in the following lines
studentsession = StudentSessions.objects.filter(session=session).select_related('student', 'session', 'session__teacher', 'session__name', 'session__day').students
self.fields['student'] = forms.ChoiceField(choices= studentsession)
as I can use the students in models
StudentSessions model in models.py
class StudentSessions(models.Model):
student = models.ForeignKey(Student, related_name='student_session', on_delete=models.CASCADE)
session = models.ForeignKey(Session, related_name='student_session', on_delete=models.CASCADE)
class Meta:
unique_together = ['student', 'session']
def __str__(self):
return f'{self.student.name}, {self.session.teacher}, {self.session.time}, {self.session.name} ' # type: ignore

How to test if an instance of a django model was created?

I am testing an api for a game and have some issues with a model.
This is my model:
models.py
class Gamesession(models.Model):
gamemode = models.ForeignKey(Gamemode, on_delete=models.CASCADE, null=True)
user = models.ForeignKey(CustomUser, on_delete=models.SET_NULL, null=True)
gametype = models.ForeignKey(Gametype, on_delete=models.CASCADE)
created = models.DateTimeField(editable=False)
objects = models.Manager()
This is the test suite:
test_models.py
def setUp(self):
self.user = CustomUser.objects.create(id=1, username="carina")
self.gametype = Gametype.objects.create(id=1, name="NewGame", rounds=5, round_duration=60, enabled=True)
self.gamesession_user = self.user
self.gamesession_gametype = self.gametype
self.gamesession_created = datetime.utcnow().replace(tzinfo=pytz.UTC)
self.gamesession = Gamesession.objects.create(id=1,
user=self.gamesession_user,
gametype=self.gamesession_gametype,
created=self.gamesession_created)
def test_create_gamesession(self):
gamesession = Gamesession.objects.create(gametype=self.gametype)
assert gamesession.gametype == self.gamesession_gametype
Running my tests keeps retrieving the error: GamesessionTests::test_create_gamesession - django.db.utils.IntegrityError: null value in column "created" of relation "frontend_games
How can I solve this? Is there a better way to test if an instance of the model is created?
This happens because you defined created as a field that is not null=True and without any default=…, so that means that it does not assign a proper value to it. You thus can construct the object with:
def test_create_gamesession(self):
gamesession = Gamesession.objects.create(
gametype=self.gametype,
gamesession_created=self.gamesession_created
)
assert gamesession.gametype == self.gamesession_gametype
You probably do not want to specify a value anyway, you can make use of auto_now_add=True [Django-doc] to specify that Django should fill in the timestamp when creating the object:
class Gamesession(models.Model):
# …
created = models.DateTimeField(auto_now_add=True)
this will also make the field non-editable.

django rest framework filter on foregin table

i'm writing API in django rest framework, i want to do a filter on a foreign key table.
want to do a filter in the scripts_details table as per the user. since scripts_details is not having user, i am trying to filter in the script tables. but it is not happening.
i tired return ScriptDetails.objects.filter(script=scriptid, script.user=self.request.user)
Models.py
class Scripts(models.Model):
tutorial = models.OneToOneField(TutorialDetail)
language = models.ForeignKey(Language)
status = models.BooleanField(default=False)
data_file = models.FileField(upload_to='scripts',blank=True)
user = models.ForeignKey(User,related_name='user_id')
class ScriptDetails(models.Model):
cue=models.TextField()
narration=models.TextField()
order=models.PositiveIntegerField()
script=models.ForeignKey('Scripts', on_delete = models.CASCADE)
serializer.py
class ScriptsDetailSerializer(serializers.ModelSerializer):
class Meta:
model=ScriptDetails
fields=('cue','narration','order','script')
views.py
class ScriptsDetailsList(generics.ListCreateAPIView):
serializer_class=ScriptsDetailSerializer
def get_queryset(self):
scriptid = self.kwargs.get('scriptid')
return ScriptDetails.objects.filter(script=scriptid)
urls.py
url(r'^api/scriptsdetails/(?P<scriptid>[0-9]+)/', views.ScriptsDetailsList.as_view())

How to make a query on related_name field?

I have to models connected by a ForeignKey
class User(AbstractUser):
...
and
class PrivateMessage(models.Model):
user_from = models.ForeignKey(
User,
verbose_name=u'From',
related_name='sent_messages',
)
user_to = models.ForeignKey(
User,
verbose_name=u'To',
related_name='received_messages',
)
Is there any way to get all the addresses for a particular user. For example, if
u = User.objects.get(id=1)
messages = PrivateMessage.objects.filter(user_from=u)
for m in messages:
users.add(m.user_to)
How to obtain a list of users that appear in user_to for these messages using only Django ORM methods?
I think a better idea would be to define ManyToManyField on the User model:
class User(AbstractUser):
#...
receivers = models.ManyToManyField('self', through='Message',
symmetrical=False, related_name="senders")
class Message(models.Model):
user_from = models.ForeignKey(MyUser, related_name='messages_from')
user_to = models.ForeignKey(MyUser, related_name='messages_to')
message = models.CharField(max_length=100, default="")
#...
Then to retrieve users list on the other end you simply do:
User.objects.get(id=1).receivers.all() # who I sent the message to
User.objects.get(id=1).senders.all() # who sent me a message
This way you have a nice clear API.
Finally, I ended up writing three queries:
users_from = set(PrivateMessage.objects.filter(
user_to=self.request.user,
).values_list(
'user_from__pk',
flat=True,
))
users_to = set(PrivateMessage.objects.filter(
user_from=self.request.user,
).values_list(
'user_to__pk',
flat=True,
))
interlocutors = User.objects.filter(pk__in=users_from.union(users_to))
I saw this docs
Maybe you can try:
u = User.objects.get(id=1)
users = User.objects.filter(received_messages__user_from=u).distinct()
related_name field makes our queries especially the ones using foreign key (on to many relation) easier, shorter and cleaner.
Let say we have 2 models classes Library and Book.
class Library(Models.model):
name = models.CharField(max_length = 100)
`class Book(Models.model):
title = models.CharField(max_length = 100)
library = models.ForeignKey(Library,
on_delete = models.CASCADE,
related_name = 'books')`
Here we have a one to many relation from Library to Book using foriegn key.
And in my django shell. I can create a new Library and a book related to that library in the following manner.
`from <app_name>.models import *`
`library = Library.objects.create(name = 'Big Library')`
`Book.objects.create(title = 'Awesome book', library = library`
Now I can query the book of the library using related name of model Book class:
`library.books.all()`
rather than using the starting the query from Book model as:
Book.objects.filter(library = library)