Соотношение «многие ко многим» в таблице «Пользователь» в Django - PullRequest
1 голос
/ 28 марта 2012

Я пишу приложение, в котором мне нужно связать данные с парами пользователей. Например, каждая пара пользователей будет иметь оценку совместимости, связанную с ними, а также отношения «многие ко многим», такие как художники, которые у них общие. Меня смущает лучший способ сделать это, кажется, что я бы использовал комбинацию: 1) расширение User посредством отношения один-к-одному, 2) использование рекурсивного отношения к себе в таблице User, 3) в сочетании с указанием дополнительных полей в отношениях M2M , но я не могу обернуться, как будет выглядеть модель.

Вот как я это делаю в настоящее время, и я полагаю, что это не лучший способ сделать это, поскольку для каждого запроса требуется два прохода через БД:

в models.py (псевдо-код, предположим, что существует класс Artist):

class UserProfile(models.Model):
    user = models.OneToOneField(User)
    zipcode = models.CharField(max_length=16)

def create_user_profile(sender, instance, created, **kwargs):
    if created:
        profile, created = UserProfile.objects.get_or_create(user=instance)

post_save.connect(create_user_profile, sender=User)         

class Score(models.Model):
    user = models.ForeignKey(User, related_name='score_first_user')
    second_user = models.ForeignKey(User, related_name='score_second_user')
    dh_score = models.DecimalField(decimal_places=2, max_digits=5)
    cre_date = models.DateTimeField(auto_now_add=True)
    upd_date = models.DateTimeField(auto_now=True)
    deleted = models.BooleanField()

    class Meta:
        unique_together = ('user', 'second_user')   

class UserArtist(models.Model):
    user = models.ForeignKey(User, related_name='userartist_first_user')
    second_user = models.ForeignKey(User, related_name='userartist_second_user')
    artist = models.ForeignKey(Artist)
    cre_date = models.DateTimeField(auto_now_add=True)
    upd_date = models.DateTimeField(auto_now=True)
    deleted = models.BooleanField()

затем в views.py я сохраняю партитуры и обычных исполнителей, используя что-то вроде (псевдокод):

s = Score(user=u, second_user=second_user score=dh_score)
s.save()

и получить их, используя что-то вроде:

u = User.objects.get(username="%s" % username)
user_scores = Score.objects.filter( Q(user=u.id) | Q(second_user=u.id) ).order_by('-dh_score')[:10]

for user_score in user_scores:
# non-relevant logic to determine who is user and who is partner
...

    partner_artists = UserArtist.objects.filter( (Q(user=u.id) & Q(second_user=partner.id))\
                                           | (Q(user=partner.id) & Q(second_user=u.id))
)

Каков наилучший способ сделать это?

1 Ответ

4 голосов
/ 03 апреля 2012

Вот как я выполнил соединение данных пользователь-пользователь, а также установил отношение M2M к промежуточной таблице:

models.py

from django.db import models
from django.contrib.auth.models import User
from django.db.models.signals import post_save

class UserProfile(models.Model):
    user = models.OneToOneField(User)
    pair = models.ManyToManyField('self', through='PairData', symmetrical=False)


    def __unicode__(self):
        return "%s's profile" % self.user

def create_user_profile(sender, instance, created, **kwargs):
    if created:
        profile, created = UserProfile.objects.get_or_create(user=instance)

post_save.connect(create_user_profile, sender=User)

class PairData(models.Model):
    first_user = models.ForeignKey(UserProfile, related_name='first_user')
    second_user = models.ForeignKey(UserProfile, related_name='second_user')
    raw_score = models.DecimalField(decimal_places=4, max_digits=9)
    dh_score = models.DecimalField(decimal_places=2, max_digits=5)
    distance = models.PositiveIntegerField()
    cre_date = models.DateTimeField(auto_now_add=True)

    def __unicode__(self):
        return u"%s %s %f %d" % (self.first_user, self.second_user, self.dh_score, self.distance)   

class Artist(models.Model):
    pair = models.ManyToManyField(PairData)
    artist_name = models.CharField(max_length=256)

    def __unicode__(self):
        return u"%s" % self.artist_name

Вот пример того, как я запросил данные пары (views.py):

def matches(request, username):
    user_profile = User.objects.get(username=username).get_profile()
    pd = PairData.objects.filter( Q(first_user=user_profile) | Q(second_user=user_profile) ).order_by('-dh_score')

и артисты, связанные с каждой парой:

def user_profile(request, username):
    user_profile = User.objects.get(username=username).get_profile()
    viewers_profile = request.user.get_profile()

    pair = PairData.objects.filter( (Q(first_user=user_profile)    & Q(second_user=viewers_profile)) \
                                  | (Q(first_user=viewers_profile) & Q(second_user=user_profile)) )

    artists = Artist.objects.filter(pair=pair)

Если есть лучший способ сделать запрос без использования Q, пожалуйста, поделитесь!

...