Django: m2m отношения создают две линии вместо одной - PullRequest
0 голосов
/ 06 декабря 2018

Я расширил UserModel следующим образом:

# users/models.py
from django.contrib.auth.models import AbstractUser
from django.db import models

class CustomUser(AbstractUser):
    # add additional fields in here
    credit = models.IntegerField(default=200)
    follow = models.ManyToManyField('self', related_name='follow')
    def __str__(self):
        return self.username

Но я застрял в том, как мне добавить / удалить подписчика.Я создал представление с:

@login_required
def follow(request, user_id):
    user = get_object_or_404(CustomUser, pk=user_id)
    if CustomUser.objects.filter(follow=user.pk).exists():
        request.user.follow.remove(user)
    else:
        request.user.follow.add(user)
    return redirect('profil', user_id)

Проблема:

Допустим, request.user.pk равно 1, а user_id равно 2.

Для add часть (в else), я ожидаю новую строку в базе данных с from_customuser_id=1 и to_customuser_id=2, однако она создает две строки:

  • одна с from_customuser_id=1 и from_customuser_id=2 как и ожидалось

  • один с from_customuser_id=2 и from_customuser_id=1, который мне не нужен.

А для части removeif) я бы ожидал, что она удалит только строку

  • from_customuser_id=1 и from_customuser_id=2

Но он удаляет две строки.

Я прочитал документ о связях моделей django , но не нашел, как решить эту проблему.

Вопрос:

Как мне обновить мой код, чтобы метод add вставлял только одну строку с from_customuser_id=1, from_customuser_id=2 и метод remove только для удаленияэта строка (при условии, что текущий пользователь имеет идентификатор 1).


Не уверен, что это актуально, но для полноты картины это связанная часть моего urls.py:

path('follow/<int:user_id>', views.follow, name='follow'),
path('unfollow/<int:user_id>', views.follow, name='unfollow'),

И вот как я их называю в шаблонах:

{% if follow %}
    <a href="{% url 'follow' user_profil.id %}">
        Unfollow {{ user_profil.username }}
    </a>
{% else %}
    <a href="{% url 'unfollow' user_profil.id %}">
        Follow {{ user_profil.username }}
    </a>
{% endif %}

1 Ответ

0 голосов
/ 06 декабря 2018

Когда у вас есть ManyToManyField, он по существу создает связь между обоими объектами.Это также позволяет вам выполнять обратный поиск.

Например:

class Person(models.Model):
    name = model.CharField(max_length=100)

class Pet(models.Model):
    owners = models.ManyToMany(Person, related_name="pets")
    name = model.CharField(max_length=100)

bob = Person.objects.create(name="Bob")
john = Person.objects.create(name="John")
kitty_kat = Pet.objects.create(name="Kitty Kat")
kitty_kat.owners.set([bob, john])

В соответствии с этими моделями один питомец может принадлежать нескольким людям, а один человек может иметь несколько питомцев.Поэтому, если я сделаю

bob.pets.all()          # I get kitty kat
kitty_kay.owners.all()  # I get bob & john

Когда предполагается, что эти отношения находятся в одной модели, вы в итоге создаете две связи.Один как обычный и один для обратного.

Например:

class Person(models.Model):
    name = model.CharField(max_length=100)
    followers = models.ManyToManyField('self', related_name='follow') 

bob = Person.objects.create(name="Bob")
john = Person.objects.create(name="John")
john.followers.add(bob)

bob.follow.all()       # I get john... notice I use follow and not followers
john.followers.all()   # I get bob

Чтобы избежать этого, вы можете передать symmetrical=False в поле, и будет создана одна строка

followers = models.ManyToManyField('self', related_name='+', symmetrical=False)

Установка для related_name значения, начинающегося с +, также предотвратит обратный поиск (который в данном случае не нужен)

...