2011-01-21 13 views
10

Mi chiedevo se esistesse un modo per utilizzare il filtro Django() sui set di query utilizzando una proprietà python generata dinamicamente utilizzando property(). Ho first_name e last_name di ogni utente e voglio filtrare in base al loro nome concatenato first_name last_name. (La ragione di questo è che quando faccio completamento automatico che cerco di vedere se la query corrisponde il nome, il cognome, o parte della concatenazione. Voglio John S per abbinare John Smith, per esempio.query django basata sulla proprietà dinamica()

ho creato una proprietà di name:.

def _get_name(self): 
    return self.first_name + " " + self.last_name 
    name = property(_get_name) 

In questo modo posso chiamare user.name per ottenere il nome concatenato

Tuttavia, se provo a fare User.objects.filter(name__istartswith=query) ottengo l'errore Cannot resolve keyword 'name' into field.

Qualche idea su come fare questo? Devo creare un altro campo nel database per memorizzare il nome completo?

risposta

9

filter() opera a livello di database (in realtà scrive SQL), quindi non sarà possibile utilizzarlo per qualsiasi query basata sul codice python (dynamic property in your question).

Questa è una risposta messo insieme da molte altre risposte in questo reparto:)

+0

Interessante, no, non puoi filtrare le proprietà, ma puoi raggruppare i campi esistenti e filtrarli dal gruppo [rispondi di seguito] (http://stackoverflow.com/a/19040011/3999748) – Persijn

0

pensa che non è possibile in Django filtrare sulle proprietà che non presenta come un database archiviato, ma cosa si può fare per rendere la ricerca fredda di completamento automatico è qualcosa di simile:

if ' ' in query: 
    query = query.split() 
    search_results = list(chain(User.objects.filter(first_name__icontains=query[0],last_name__icontains=query[1]), 
            User.objects.filter(first_name__icontains=query[1],last_name__icontains=query[0]))) 
else: 
    search_results = User.objects.filter(Q(first_name__icontains=query)| Q(last_name__icontains=query)) 

Questo codice dà all'utente del tuo sistema una flessibilità per iniziare a digitare il nome o il cognome e l'utente sarà grato a te per averlo permesso.

+0

pensato a questo, ma ci sono alcuni problemi. Alcune persone possono avere spazi nel loro nome o cognome, e quindi dividerlo per spazio potrebbe essere rotto. Altrimenti, però, ho già cercato nome o cognome. Ho impostato un segnale per popolare il campo del nome al momento della creazione, però, quindi funziona ora. Grazie. – munchybunch

12

La risposta accettata, non è del tutto vero.

Per molti casi, è possibile ignorare get() nel modello manager per pop proprietà dinamiche dalla chiave argomenti, quindi aggiungere gli attributi reali che si desidera interrogare contro nel dizionario argomenti kwargs parola chiave. Assicurati di restituire un valore super in modo che le normali chiamate get() restituiscano il risultato previsto.

Sto solo incollando la mia soluzione, ma per il __startswith e altre query condizionali è possibile aggiungere un po 'di logica a split il doppio-underscore e gestire in modo appropriato.

Qui era il mio work-around per consentire l'esecuzione di query da una proprietà dinamica:

class BorrowerManager(models.Manager): 
    def get(self, *args, **kwargs): 
     full_name = kwargs.pop('full_name', None) 
     # Override #1) Query by dynamic property 'full_name' 
     if full_name: 
      names = full_name_to_dict(full_name) 
      kwargs = dict(kwargs.items() + names.items()) 
     return super(BorrowerManager, self).get(*args, **kwargs) 

In models.py:

class Borrower(models.Model): 
    objects = BorrowerManager() 

    first_name = models.CharField(null=False, max_length=30) 
    middle_name = models.CharField(null=True, max_length=30) 
    last_name = models.CharField(null=False, max_length=30) 
    created = models.DateField(auto_now_add=True) 

In utils.py (per la Motivo del contesto):

def full_name_to_dict(full_name): 
    ret = dict() 
    values = full_name.split(' ') 
    if len(values) == 1: 
     raise NotImplementedError("Not enough names to unpack from full_name") 
    elif len(values) == 2: 
     ret['first_name'] = values[0] 
     ret['middle_name'] = None 
     ret['last_name'] = values[1] 
     return ret 
    elif len(values) >= 3: 
     ret['first_name'] = values[0] 
     ret['middle_name'] = values[1:len(values)-1] 
     ret['last_name'] = values[len(values)-1] 
     return ret 
    raise NotImplementedError("Error unpacking full_name to first, middle, last names") 
+2

Buono! per poter usare qualcosa come 'isinstance (getattr (self.model, attr), proprietà)' per verificare se l'argomento è proprietà o no. Sto facendo qualcosa in modo simile .. –

+1

'kwargs = dict (kwargs.items() + names.items())' può essere scritto come 'kwargs.update (names)' – thebjorn

+1

questo sembra non funzionare più in Django 1.9 –

3

Ho avuto un problema simile e cercavo una soluzione.Dando per scontato che un motore di ricerca sarebbe l'opzione migliore (ad esempio django-haystack con Elasticsearch), è così che vorrei implementare un certo codice per le vostre esigenze utilizzando solo il Django ORM (si può sostituire icontains con istartswith):

from django.db.models import Value 
from django.db.models.functions import Concat 

queryset = User.objects.annotate(full_name=Concat('first_name', Value(' '), 'last_name') 
return queryset.filter(full_name__icontains=value) 

Nel mio caso non sapevo se l'utente inserisse "first_namelast_name" o viceversa, quindi ho usato il seguente codice.

from django.db.models import Q, Value 
from django.db.models.functions import Concat 

queryset = User.objects.annotate(first_last=Concat('first_name', Value(' '), 'last_name'), last_first=Concat('last_name', Value(' '), 'first_name')) 
return queryset.filter(Q(first_last__icontains=value) | Q(last_first__icontains=value)) 

Con Django < 1.8, si sarebbe probabilmente bisogno di ricorrere alla extra con la funzione SQL CONCAT, qualcosa di simile al seguente:

queryset.extra(where=['UPPER(CONCAT("auth_user"."last_name", \' \', "auth_user"."first_name")) LIKE UPPER(%s) OR UPPER(CONCAT("auth_user"."first_name", \' \', "auth_user"."last_name")) LIKE UPPER(%s)'], params=['%'+value+'%', '%'+value+'%']) 
Problemi correlati