Opened 3 years ago

Closed 3 years ago

#18056 closed Bug (fixed)

query_set.dates('field_name', 'years') does not return distinct elements

Reported by: framazz@… Owned by: nmfm
Component: Database layer (models, ORM) Version: 1.3
Severity: Normal Keywords:
Cc: Triage Stage: Accepted
Has patch: no Needs documentation: no
Needs tests: no Patch needs improvement: no
Easy pickings: no UI/UX: no

Description

I have a model (Paziente) with a date field (arrivo)

If I issue: years = Paziente.objects.all().dates('arrivo', 'year')

then I get

[datetime.datetime(2009, 1, 1, 0, 0), datetime.datetime(2009, 1, 1, 0, 0), datetime.datetime(2009, 1, 1, 0, 0), datetime.datetime(2010, 1, 1, 0, 0), datetime.datetime(2010, 1, 1, 0, 0), datetime.datetime(2010, 1, 1, 0, 0), datetime.datetime(2010, 1, 1, 0, 0), datetime.datetime(2010, 1, 1, 0, 0), datetime.datetime(2010, 1, 1, 0, 0), datetime.datetime(2010, 1, 1, 0, 0), datetime.datetime(2010, 1, 1, 0, 0), datetime.datetime(2011, 1, 1, 0, 0), datetime.datetime(2011, 1, 1, 0, 0), datetime.datetime(2011, 1, 1, 0, 0), datetime.datetime(2011, 1, 1, 0, 0), datetime.datetime(2011, 1, 1, 0, 0), datetime.datetime(2011, 1, 1, 0, 0)]

As you can see, the years are repeated many times and are not a distinct set

I'm using the deb package for wheezy

Package: python-django

Version: 1.3.1-4

Same problem with 1.3 source code downloaded from site

Change History (8)

comment:1 Changed 3 years ago by kmtracey

  • Needs documentation unset
  • Needs tests unset
  • Patch needs improvement unset
  • Resolution set to needsinfo
  • Status changed from new to closed

I cannot recreate with the provided information. Please post the full model definition for Paziente and also let us know what database you are using.

comment:2 Changed 3 years ago by Francesca Mazzoni <framazz@…>

I'm using MySQL Server version: 5.1.61-2-log (Debian)

Here you find the complete code for Paziente and the respective PazienteAdmin:

Please let me know if you need something else

Code highlighting:

class PazienteManager(models.Manager):
    def get_query_set(self):
        return super(PazienteManager, self).get_query_set().annotate(
            models.Count('visite'))

# definisce il nome per il file della prescrizione motoria di una paziente
def moto_file_name(instance, filename):
    return '/'.join([str(instance.centro.id), 'pazienti', instance.user.username,
        'prescrizione_motoria.pdf'])

# definisce il nome per il file del protocollo nutrizionale di una paziente
def dieta_file_name(instance, filename):
    return '/'.join([str(instance.centro.id), 'pazienti', instance.user.username, 
        'protocollo_nutrizionale.pdf'])

class Paziente(models.Model):
    
    SCELTE_AGE = (
        ('a', '<36 aa'), 
        ('b', '36-39 aa'),
        ('c', '40-49 aa'),
        ('d', '50-59 aa'),
        ('e', '> 60 aa'),
        ('X', 'NON INSERITO'),
    )
    
    SCELTE_RACE = (
        ('a', 'WHITE'), 
        ('b', 'BLACK/OTHER'),
        ('X', 'NON INSERITO'),
    )
    
    SCELTE_STAGE = (
        ('a', 'LOCALIZED (ALN NEGATIVE)'), 
        ('b', 'AXILLARY LYMPH NODE POSITIVE/DISTANT METASTASIS'),
        ('c', 'STAGE UNKNOWN'),
        ('X', 'NON INSERITO'),
    )
    
    SCELTE_HORMON = (
        ('a', 'ER or PR +'), 
        ('b', 'ER-PR-'),
        ('c', 'ER or PR unknown'),
        ('X', 'NON INSERITO'),
    )

    SCELTE_HISTOLOGY= (
        ('a', 'DUCTAL'), 
        ('b', 'LOBULAR'),
        ('c', 'OTHER'),
        ('X', 'NON INSERITO'),
    )
    
    SCELTE_FAMILIAL= (
        ('a', 'HBC/HBOC'), 
        ('b', 'SHBC/SHBOC'),
        ('c', 'FBC/FBOC'),
        ('d', 'SFBC(+)/SFBOC(+)'),
        ('e', 'EOBC'),
        ('f', 'NONE'),
        ('X', 'NON INSERITO'),
    )

    nome = models.CharField(max_length=255)
    cognome = models.CharField(max_length=255)
    email = models.EmailField(blank=True, verbose_name='e-mail')
    age = models.CharField(max_length=1, verbose_name=u'age at diagnosis', 
        choices=SCELTE_AGE, default='d')
    race = models.CharField(max_length=1, choices=SCELTE_RACE, default='a')
    stage = models.CharField(max_length=1, verbose_name=u'cancer stage at diagnosis',
        choices=SCELTE_STAGE, default='a')
    hormon = models.CharField(max_length=1, verbose_name=u'hormon receptor status', 
        choices=SCELTE_HORMON, default='a')
    histologic_type = models.CharField(max_length=1, choices=SCELTE_HISTOLOGY, 
        default='a')
    familial = models.CharField(max_length=1, verbose_name=u'familial/ hereditary \
        breast cancer', choices=SCELTE_FAMILIAL, default='f')
    arrivo = models.DateField(verbose_name='data di arrivo')
    nascita = models.DateField(verbose_name='data di nascita')
    aderente = models.BooleanField(default=False)
    uscita = models.BooleanField(default=False)
    terminato = models.BooleanField(default=False)
    centro = models.ForeignKey(Centro, default=1, related_name='pazienti')
    user = models.ForeignKey(User, unique=True, related_name='pazienti')
    statura = models.DecimalField(decimal_places=2, max_digits=3, 
        verbose_name='statura (m)')
    moto = models.FileField(upload_to=moto_file_name, storage=OverwriteStorage(),
        blank=True, verbose_name='prescrizione motoria')    
    dieta = models.FileField(upload_to=dieta_file_name, storage=OverwriteStorage(),
        blank=True)
    antiipertensivi = models.BooleanField(default=False, 
        verbose_name='farmaci antiipertensivi')
    antilipemici = models.BooleanField(default=False,
        verbose_name='farmaci antilipemici')
    antidiabetici = models.BooleanField(default=False, 
        verbose_name='farmaci antidiabetici')
    tiroidei = models.BooleanField(default=False, 
        verbose_name='farmaci tiroidei')
    gastroprotettori = models.BooleanField(default=False, 
        verbose_name='farmaci gastroprotettori')
    ansiolitici = models.BooleanField(default=False, 
        verbose_name='farmaci ansiolitici/antidepressivi')
    altri_farmaci = models.TextField(blank=True)
    terapia_chirurgica = models.BooleanField(default=True)
    radioterapia = models.BooleanField(default=False)
    chemioterapia = models.BooleanField(default=False)
    ormonoterapia = models.BooleanField(default=True)
    calorie = models.IntegerField(blank=True, null=True,
        verbose_name='calorie totali')
    proteine = models.DecimalField(decimal_places=2, max_digits=7, blank=True, 
        null=True)
    CHO = models.DecimalField(decimal_places=2, max_digits=7, blank=True, 
        null=True)
    lipidi = models.DecimalField(decimal_places=2, max_digits=7, blank=True, 
        null=True)
    note = models.TextField(blank=True)
    prox_app = models.DateField(blank=True, null=True, 
        verbose_name='prossimo appuntamento')
    note_prox_app = models.TextField(blank=True, 
        verbose_name='note per il prossimo appuntamento')
    dieta1_prox_app = models.TextField(blank=True, 
        verbose_name=u'1° compito nutrizionale')
    dieta2_prox_app = models.TextField(blank=True, 
        verbose_name=u'2° compito nutrizionale')
    moto1_prox_app = models.TextField(blank=True, 
        verbose_name=u'1° compito motorio')
    moto2_prox_app = models.TextField(blank=True, 
        verbose_name=u'2° compito motorio')
    controllare = models.BooleanField(default=False)
    noterapia = models.BooleanField(default=False, verbose_name='NON INSERITO')

    def __unicode__(self):
        return self.cognome + ' ' + self.nome

    class Meta:
        ordering = ('user__last_name', 'user__first_name', 'user__username')
        verbose_name_plural = 'pazienti'

    objects = PazienteManager()

    def numero_visite(self):
        return Visita.objects.filter(paziente=self).count()
    numero_visite.admin_order_field = 'visite__count'
    numero_visite.short_description = 'Numero di visite e controlli sostenuti'

    # sovrascrivo il metodo clean per poter validare il modello    
    def clean(self):
        # errore se è aderente e mancano dati nella dieta
        if self.aderente and (self.calorie is None or self.lipidi is None or 
            self.CHO is None or self.proteine is None):
            raise ValidationError(u'Mancano uno o più dati nella dieta')

        # errore se non ho definito il prossimo appuntamento, ma le sue note sì
        if not self.prox_app and self.note_app:
            raise ValidationError(u'Non si possono inserire le note per il prossimo \
                appuntamento se non ne è stata stabilita la data')
                
        # errore se lo user è già un paziente
        if self.user.medici.all().count() > 0:
            raise ValidationError(u"L'utente selezionato è un medico, non può essere"
                " anche un paziente")        


    def save(self):

        # scrivo nome e cognome in maiuscolo
        self.nome = self.nome.upper()
        self.cognome = self.cognome.upper()
        
        # salvo il paziente
        super(Paziente, self).save()
        user.save()

class PazienteAdmin(admin.ModelAdmin):

    formfield_overrides = {
        models.TextField: {'widget': Textarea(attrs={'rows':1, 'cols':80})},
  }

    save_on_top = True
    raw_id_fields = ('user',)
    list_display = ('id', 'cognome', 'nome', user_username, 'arrivo', 'nascita', 
        'aderente', 'uscita', 'terminato', 'numero_visite', 'controllare')
    list_display_links = ('id', user_username, 'nome', 'cognome', 'arrivo',
         'nascita')
    search_fields = ('user__username', 'nome', 'cognome')
    list_filter = ('uscita', 'terminato', 'controllare')
    date_hierarchy = 'arrivo'
    ordering = ('cognome', 'nome')
    inlines = [VisitaInLineAdmin, QuestionarioInLineAdmin]
    radio_fields = {'age': admin.HORIZONTAL, 'race': admin.HORIZONTAL,
        'stage': admin.HORIZONTAL, 'hormon': admin.HORIZONTAL, 
        'histologic_type': admin.HORIZONTAL, 'familial': admin.HORIZONTAL}
    fieldsets = [
        ('', {'fields': [('user', 'email'), ('nome', 'cognome'), ( 'arrivo', 
            'nascita'), ('aderente', 'uscita', 
            'terminato', 'statura'), ('moto', 'dieta')]}),
        ('Prossimo appuntamento', {'fields': ['prox_app', 'note_prox_app', 
        'dieta1_prox_app', 'dieta2_prox_app', 'moto1_prox_app', 'moto2_prox_app']}),
        ('Caratteristiche tumore', {'fields': ['age', 'race', 'stage', 'hormon', 
            'histologic_type', 'familial'], 'classes': ['collapse']}),
        ('Terapia pregressa', {'fields': [('terapia_chirurgica', 'radioterapia', 
            'chemioterapia', 'ormonoterapia', 'noterapia')], 'classes': ['collapse']}),
        ('Dieta', {'fields': [('calorie', 'proteine', 'CHO', 'lipidi')], 
            'classes': ['collapse']}),
        ('Note', {'fields': ['note'], 'classes': ['collapse']}),
    ]

comment:3 Changed 3 years ago by anonymous

  • Resolution needsinfo deleted
  • Status changed from closed to reopened

Reopening since requested info has been provided.

comment:4 Changed 3 years ago by Nuno Maltez <nuno@…>

  • Owner changed from nobody to nmfm
  • Status changed from reopened to new

From my local tests, having a custom object manager is creating the issue. I'll take a closer look.

comment:5 Changed 3 years ago by Nuno Maltez <nuno@…>

  • Triage Stage changed from Unreviewed to Ready for checkin

comment:6 Changed 3 years ago by aaugustin

  • Triage Stage changed from Ready for checkin to Accepted

Please don't mark your own patches as RFC. Could you have someone else review it, and if it's indeed commit-ready, mark it as RFC again?

comment:7 Changed 3 years ago by Nuno Maltez <nuno@…>

Ah, sorry, it seems I misunderstood how these states are supposed to work.

comment:8 Changed 3 years ago by Anssi Kääriäinen <akaariai@…>

  • Resolution set to fixed
  • Status changed from new to closed

In [bebbbb7af096ecffc0d7585617fdfacb196bc7c2]:

Fixed #18056 - Cleared aggregations on DateQuery.add_date_select

Cleared aggregations on add_date_select method so only distinct dates
are returned when dealing with a QuerySet that contained aggregations.
That would cause the query set to return repeated dates because it
would look for distinct (date kind, aggregation) pairs.

Note: See TracTickets for help on using tickets.
Back to Top