2010-05-28 5 views
2

Disons que je l'ai défini ce modèle:forme normale élégante disjonctif dans Django

class Identifier(models.Model): 
    user = models.ForeignKey(User) 
    key = models.CharField(max_length=64) 
    value = models.CharField(max_length=255) 

Chaque utilisateur aura plusieurs identifiants, chacun avec une clé et une valeur. Je suis sûr à 100% que je veux garder le design comme ça, il y a des raisons externes pour lesquelles je le fais que je ne vais pas passer par ici, donc je ne suis pas intéressé à changer cela.

Je voudrais développer une fonction de ce genre:

def get_users_by_identifiers(**kwargs): 
    # something goes here 
    return users 

La fonction retournera tous les utilisateurs qui ont l'une des paires clé = valeur spécifiée dans kwargs **. Voici un exemple d'utilisation:

get_users_by_identifiers(a=1, b=2) 

Ceci devrait retourner tous les utilisateurs pour lesquels a = 1 ou b = 2. J'ai remarqué que la façon dont je l'ai mis tout ça, cela revient à une forme normale disjonctive ... la requête SQL serait quelque chose comme:

SELECT DISTINCT(user_id) FROM app_identifier 
    WHERE (key = "a" AND value = "1") OR (key = "b" AND value = "2") ... 

Je me sens comme il doit y avoir une façon élégante de prenez l'entrée ** kwargs et faites-y un filtre Django, en seulement 1-2 lignes, pour produire ce résultat. Je suis nouveau à Django, donc je ne suis pas sûr de savoir comment le faire. Voici ma fonction maintenant, et je suis complètement sûr que ce n'est pas la meilleure façon de le faire :)

def get_users_by_identifiers(**identifiers): 
    users = [] 
    for key, value in identifiers.items(): 
     for identifier in Identifier.objects.filter(key=key, value=value): 
      if not identifier.user in users: 
       users.append(identifier.user) 

    return users 

Des idées? :)

Merci!

Répondre

1
def get_users_by_identifiers(**kwargs): 
    q = reduce(operator.or_, Q(identifier__key=k, identifier__value=v) 
     for (k, v) in kwargs.iteritems()) 
    return User.objects.filter(q) 
+0

Merci! Excellent moyen de le faire. J'ai dû légèrement le modifier cependant. Comme vous l'avez écrit, pour moi, il s'est traduit par "SyntaxError: L'expression du générateur doit être entre parenthèses sinon l'unique argument". J'ai changé la ligne sceond pour lire "q = réduire (operator.or_, (Q (identifiant__key = k, identifiant__value = v) pour (k, v) dans kwargs.iteritems()))" et cela a fonctionné parfaitement. – MikeC8

+0

En fait, encore une petite modification nécessaire pour le faire fonctionner exactement comme je l'ai spécifié ci-dessus. Vous devez changer la dernière ligne pour lire "return User.objects.filter (q) .distinct()" – MikeC8

Questions connexes