#27982: Possible race condition related to queryset union
-------------------------------------+-------------------------------------
Reporter: gigelu | Owner: nobody
Type: Bug | Status: new
Component: Database layer | Version: 1.11
(models, ORM) |
Severity: Normal | Resolution:
Keywords: | Triage Stage:
| Unreviewed
Has patch: 0 | Needs documentation: 0
Needs tests: 0 | Patch needs improvement: 0
Easy pickings: 0 | UI/UX: 0
-------------------------------------+-------------------------------------
Description changed by gigelu:
Old description:
> I found a strange bug: the results from a paginator are affected randomly
> by setting breakpoints in the IDE.
>
> The bug appears '''only''' when I am using `union` on a queryset.
>
> I am using Django 1.11rc1 with DRF 3.6.2.
>
> The models:
> {{{
> class BaseNotification(models.Model):
> user = models.ForeignKey(settings.AUTH_USER_MODEL,
> on_delete=models.CASCADE)
> type = models.CharField(max_length=10, choices=TYPES,
> default=TYPE_SIMPLE, blank=True)
> popup_type = models.CharField(max_length=10, choices=POPUP_TYPES,
> default=POPUP_DEFAULT)
> title = models.CharField(max_length=200, blank=True)
> message = models.TextField()
> completed = models.BooleanField(blank=True, default=False)
> date_completed = models.DateTimeField(null=True, blank=True,
> default=None)
> date_created = models.DateTimeField(auto_now_add=True)
>
> class SimpleNotification(BaseNotification):
>
> def save(self, *args, **kwargs):
> self.type = self.TYPE_SIMPLE
> super().save(*args, **kwargs)
>
> class DecisionNotification(BaseNotification):
> ... (not relevant)
> }}}
>
> The view:
> {{{
> class NotificationView(ListAPIView):
> permission_classes = [IsAuthenticated]
> filter_backends = [OrderingFilter]
> ordering_fields = ['completed', 'date_created', 'type']
> ordering = ['-completed', '-date_created']
> serializer_class = NotificationSerializer
>
> def get_queryset(self):
> qs1 =
> SimpleNotification.objects.filter(user=self.request.user).only(
> 'type', 'popup_type', 'completed', 'date_created')
> qs2 =
> DecisionNotification.objects.filter(user=self.request.user).only(
> 'type', 'popup_type', 'completed', 'date_created')
> qs = qs1.union(qs2)
> return qs
> }}}
>
> The serializer:
> {{{
> class NotificationSerializer(serializers.ModelSerializer):
> title = serializers.CharField(source='the_title')
>
> class Meta:
> model = SimpleNotification
> fields = ['type', 'popup_type', 'title', 'url', 'completed',
> 'date_created']
> }}}
>
> The problem: I have 5 notifications in DB (3 simple, 2 decision), but it
> returns only the first one in normal usage, and sometimes all or
> sometimes one when I am using breakpoints.
>
> I've isolated the problem near this line of code:
> https://github.com/tomchristie/django-rest-
> framework/blob/master/rest_framework/pagination.py#L208. If I put a
> breakpoint inside the paginator's init
> (https://github.com/django/django/blob/master/django/core/paginator.py#L29)
> I get the correct result every time.
>
> http://i.imgur.com/9zoikMQ.png
> http://i.imgur.com/bsHgxig.png
>
> I've also attached a video showing the results while using different
> breakpoints.
New description:
I found a strange bug: the results from a paginator are affected randomly
by setting breakpoints in the IDE.
The bug appears '''only''' when I am using `union` on a queryset.
I am using Django 1.11rc1 with DRF 3.6.2.
The models:
{{{
class BaseNotification(models.Model):
user = models.ForeignKey(settings.AUTH_USER_MODEL,
on_delete=models.CASCADE)
type = models.CharField(max_length=10, choices=TYPES,
default=TYPE_SIMPLE, blank=True)
popup_type = models.CharField(max_length=10, choices=POPUP_TYPES,
default=POPUP_DEFAULT)
title = models.CharField(max_length=200, blank=True)
message = models.TextField()
completed = models.BooleanField(blank=True, default=False)
date_completed = models.DateTimeField(null=True, blank=True,
default=None)
date_created = models.DateTimeField(auto_now_add=True)
class SimpleNotification(BaseNotification):
def save(self, *args, **kwargs):
self.type = self.TYPE_SIMPLE
super().save(*args, **kwargs)
class DecisionNotification(BaseNotification):
... (not relevant)
}}}
The view:
{{{
class NotificationView(ListAPIView):
permission_classes = [IsAuthenticated]
filter_backends = [OrderingFilter]
ordering_fields = ['completed', 'date_created', 'type']
ordering = ['-completed', '-date_created']
serializer_class = NotificationSerializer
def get_queryset(self):
qs1 =
SimpleNotification.objects.filter(user=self.request.user).only(
'type', 'popup_type', 'completed', 'date_created')
qs2 =
DecisionNotification.objects.filter(user=self.request.user).only(
'type', 'popup_type', 'completed', 'date_created')
qs = qs1.union(qs2)
return qs
}}}
The serializer:
{{{
class NotificationSerializer(serializers.ModelSerializer):
title = serializers.CharField(source='the_title')
class Meta:
model = SimpleNotification
fields = ['type', 'popup_type', 'title', 'url', 'completed',
'date_created']
}}}
The problem: I have 5 notifications in DB (3 simple, 2 decision), but it
returns only the first one in normal usage, and sometimes all or sometimes
one when I am using breakpoints.
I've isolated the problem near this line of code:
https://github.com/tomchristie/django-rest-
framework/blob/master/rest_framework/pagination.py#L208. If I put a
breakpoint inside the paginator's init
(https://github.com/django/django/blob/master/django/core/paginator.py#L29)
I get the correct result every time.
http://i.imgur.com/9zoikMQ.png
http://i.imgur.com/bsHgxig.png
--
--
Ticket URL: <https://code.djangoproject.com/ticket/27982#comment:1>
Django <https://code.djangoproject.com/>
The Web framework for perfectionists with deadlines.
--
You received this message because you are subscribed to the Google Groups
"Django updates" group.
To unsubscribe from this group and stop receiving emails from it, send an email
to [email protected].
To post to this group, send email to [email protected].
To view this discussion on the web visit
https://groups.google.com/d/msgid/django-updates/064.d9e34f4d5b1e5df123aa94f96c457bb6%40djangoproject.com.
For more options, visit https://groups.google.com/d/optout.