LoginSignup
13
15

More than 5 years have passed since last update.

Djangoでお気に入り機能実装

Posted at

今回は
Djangoでお気に入り登録機能を実装する.
ということで,フォロー機能とも言えます.物は言いようですね!

環境

Python3.6
Django2.1

大まかな流れ

Placeという名前のモデル(観光スポットとか)をお気に入り登録すると想定しています.アプリ名はmyappとしています.

単純な流れかと思います.
1. ユーザーがブラウザ上でお気に入り登録したいPlaceのpkをPOST
2. views.pyでpkに対応するPlaceをCustomUserモデルのfavorite_placeフィールドに追加

コード

最小限の内容だけ書いています.

url

urls.pyは以下の部分をurlpatterns=[]に追加しましょう.

urls.py
    path('follow/<int:pk>/', views.followPlace, name='follow'),   

html

ListViewなどであればこのような形でフォームを作れば簡単にpkをPOSTできます.

ex.html
<form action="{% url 'myapp:follow' place.pk %}" method="post">
    <button type="submit" name="button">お気に入り追加</button>
    {% csrf_token %}
</form>

view

汎用ビューを使うべきだと思いますが,関数ビューだとシンプルに作れます.
request.userでリクエストを送ったユーザーモデルを参照できます.

views.py
from django.shortcuts import redirect, get_object_or_404

def followPlace(request, pk):
    """場所をお気に入り登録する"""
    place = get_object_or_404(Place, pk=pk)
    request.user.favorite_place.add(place)
    return redirect('myapp:index')

model

長いです.

models.py
from django.db import models
from django.core.mail import send_mail
from django.contrib.auth.models import PermissionsMixin,UserManager
from django.contrib.auth.base_user import AbstractBaseUser,BaseUserManager
from django.utils.translation import ugettext_lazy as _
from django.utils import timezone
from django.contrib.auth.validators import UnicodeUsernameValidator

"""場所の情報"""
class Place(models.Model):
    name = models.CharField('名前',max_length = 30)
    address = models.CharField('住所',max_length=100)

    def __str__(self):
        return self.name

"""書かないとcreatesuperuserできない"""
class UserManager(BaseUserManager):
    use_in_migrations = True

    def _create_user(self, username, email, password, **extra_fields):
        if not username:
            raise ValueError('The given username must be set')
        email = self.normalize_email(email)
        username = self.model.normalize_username(username)
        user = self.model(username=username, email=email, **extra_fields)
        user.set_password(password)
        user.save(using=self._db)
        return user

    def create_user(self, username, email=None, password=None, **extra_fields):
        extra_fields.setdefault('is_staff', False)
        extra_fields.setdefault('is_superuser', False)
        return self._create_user(username, email, password, **extra_fields)

    def create_superuser(self, username, email, password, **extra_fields):
        extra_fields.setdefault('is_staff', True)
        extra_fields.setdefault('is_superuser', True)

        if extra_fields.get('is_staff') is not True:
            raise ValueError('Superuser must have is_staff=True.')
        if extra_fields.get('is_superuser') is not True:
            raise ValueError('Superuser must have is_superuser=True.')

        return self._create_user(username, email, password, **extra_fields)

"""お気に入り機能追加済みユーザー"""
class CustomUser(AbstractBaseUser, PermissionsMixin):
    username_validator = UnicodeUsernameValidator()

    username = models.CharField(
        _('username'),
        max_length=150,
        unique=True,
        help_text=_('Required. 150 characters or fewer. Letters, digits and @/./+/-/_ only.'),
        validators=[username_validator],
        error_messages={
            'unique': _("A user with that username already exists."),
        },
    )
    first_name = models.CharField(_('first name'), max_length=30, blank=True)
    last_name = models.CharField(_('last name'), max_length=150, blank=True)
    email = models.EmailField(_('email address'), blank=True)

    favorite_place = models.ManyToManyField(Place, verbose_name='お気に入りの場所', blank=True)

    is_staff = models.BooleanField(
        _('staff status'),
        default=False,
        help_text=_('Designates whether the user can log into this admin site.'),
    )
    is_active = models.BooleanField(
        _('active'),
        default=True,
        help_text=_(
            'Designates whether this user should be treated as active. '
            'Unselect this instead of deleting accounts.'
        ),
    )
    date_joined = models.DateTimeField(_('date joined'), default=timezone.now)

    objects = UserManager()

    EMAIL_FIELD = 'email'
    USERNAME_FIELD = 'username'
    REQUIRED_FIELDS = ['email']

    class Meta:
        verbose_name = _('user')
        verbose_name_plural = _('users')

    def clean(self):
        super().clean()
        self.email = self.__class__.objects.normalize_email(self.email)

    def get_full_name(self):
        """
        Return the first_name plus the last_name, with a space in between.
        """
        full_name = '%s %s' % (self.first_name, self.last_name)
        return full_name.strip()

    def get_short_name(self):
        """Return the short name for the user."""
        return self.first_name

    def email_user(self, subject, message, from_email=None, **kwargs):
        """Send an email to this user."""
        send_mail(subject, message, from_email, [self.email], **kwargs)

大掛かりに書いていますがUserモデルに以下のフィールドを追加しただけで,あとはデフォと同じです.
リレーションは多対多ですね.
あるユーザーは複数の場所をお気に入り出来るし
また,ある場所は複数のユーザーにお気に入り登録されるからです.

favorite_place = models.ManyToManyField(Place, verbose_name='お気に入りの場所', blank=True)

まとめ

pkをPOSTして処理するだけなのですが便利なので記事にしました.
ちなみにユーザー同士のフォローについては,もう少し込み入った話になってきます.

以上です.ありがとうございました.

13
15
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
13
15