Warning: count(): Parameter must be an array or an object that implements Countable in /home/xs638785/agile-software.site/public_html/wp-content/plugins/rich-table-of-content/functions.php on line 490
仮想環境
仮想環境構築
python3 -m venv myvenv
仮想環境構築
source myvenv/bin/activate
requirements.txt
Djangoで画像ファイルを扱えるようにするためにpillowをインストールしておきます。
pip3 install -r requirements.txt
Django~=3.1.4
djangorestframework==3.11.0
pillow~=8.1.0
プロジェクト作成
django-admin startproject sns .
アプリケーション作成
django-admin startapp core
django-admin startapp api_user
django-admin startapp api_dm
settings.py
INSTALLED_APPS = [
'rest_framework',
'rest_framework.authtoken',
'core.apps.CoreConfig',
'api_user.apps.ApiUserConfig',
'api_dm.apps.ApiDmConfig',
]
TIME_ZONE = 'ASIA/TOKYO'
ローカルホストの末尾にmedia
MEDIA_ROOT = os.path.join(BASE_DIR, '_media')
MEDIA_URL = '/media/'
coreフォルダで作成したUserモデルをデフォルトで使用するために下記のコードを追記する必要があります。
AUTH_USER_MODEL = 'core.User'
urls.py
api_userとapi_dmフォルダにurls.pyを作成します。
rest_frameworkに搭載されているobtain_auth_tokenを使用します。
staticファイルのurlパターンを簡単に生成してくれるメソッド
from django.urls import path, include
from rest_framework.authtoken import views
from django.conf import settings
from django.conf.urls.static import static
urlpatterns = [
path('authen/', views.obtain_auth_token),
path('api/user/', include('api_user.urls')),
path('api/dm/', include('api_dm.urls')),
]
urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
ModelViewSet系ビューにおける一覧・詳細・登録・更新・一部更新・削除のURLパターンをまとめて追加することができます。
router = DefaultRouter()
router.register('profile',views.ProfileViewSet)
router.register('approval', views.FriendRequestViewSet)
URLパターンを登録
from django.urls import path, include
from api_user import views
from rest_framework.routers import DefaultRouter
app_name = 'user'
router = DefaultRouter()
router.register('profile',views.ProfileViewSet)
router.register('approval', views.FriendRequestViewSet)
urlpatterns = [
path('create/', views.CreateUserView.as_view(), name='create'),
path('myprofile/', views.MyProfileListView.as_view(), name='myprofile'),
path('',include(router.urls))
]
from django.urls import path, include
from rest_framework.routers import DefaultRouter
from api_dm import views
app_name = 'dm'
router = DefaultRouter()
router.register('message', views.MessageViewSet, basename="message")
router.register('inbox', views.InboxListView, basename='inbox')
urlpatterns = [
path('',include(router.urls))
]
models.py
User
create_userはあらかじめBaseUserManagerで定義されている。
emailが入されなかった場合はエラーを表示するように設定。
大文字で入力された場合に小文字に変換してからデータベースに登録するようにします。
class UserManager(BaseUserManager):
def create_user(self, email, password=None, **extra_fields):
if not email:
raise ValueError('email is must')
user = self.model(email=self.normalize_email(email), **extra_fields)
user.set_password(password)
user.save(using=self._db)
return user
email認証に変更しているためsuperuserの記述が必要になります。
オーバーライドした部分についてはデータベースにsaveします。
def create_superuser(self, email, password):
user = self.create_user(email, password)
user.is_staff = True
user.is_superuser = True
user.save(using= self._db)
return user
uniqueをtrueにしていることで違うユーザーが同じemailで新規アカウントを作ることができないようにしています。
- AbstractBaseUser
- PermissionsMixin:
- BaseUserManager
AbstractBaseUserはパーミッションの機能を持っていないため同時に継承 する必要があります。
class User(AbstractBaseUser, PermissionsMixin):
email = models.EmailField(max_length=50, unique=True)
is_active = models.BooleanField(default=True)
is_staff = models.BooleanField(default=False)
objects = UserManager()
USERNAME_FIELD = 'email'
def __str__(self):
return self.email
Profile
一人のユーザーに対して一つのプロフィールをOnetoOneFieldで定義。
参照先のユーザーが削除された場合プロフィールも削除されるようにon_deleteを作成します。
auto_now_add=Trueにすることでプロフィールを作成したときの時刻を登録
プロフィール画像を保存するための設定
class Profile(models.Model):
nickName = models.CharField(max_length=20)
userPro = models.OneToOneField(
settings.AUTH_USER_MODEL, related_name='userPro',
on_delete=models.CASCADE
)
created_on = models.DateTimeField(auto_now_add=True)
img = models.ImageField(blank=True, null=True, upload_to=upload_path)
def __str__(self):
return self.nickName
upload_pathの指定
拡張子だけを取り出すことが出来ます。
mediaフォルダのimageフォルダに画像を格納するようにします。
def upload_path(instance, filename):
ext = filename.split('.')[-1]
return '/'.join(['image', str(instance.userPro.id)+str(instance.nickName)+str(".")+str(ext)])
FriendRequest
ユーザーの中から1人を選ぶためにForeignKeyを使います。
1度だけ申請を行うように行うようにaskFromとaskToの組み合わせが重複しないように
class Meta:
unique_together = (('askFrom', 'askTo'),)
class FriendRequest(models.Model):
askFrom = models.ForeignKey(
settings.AUTH_USER_MODEL, related_name='askFrom',
on_delete=models.CASCADE
)
askTo = models.ForeignKey(
settings.AUTH_USER_MODEL, related_name='askTo',
on_delete=models.CASCADE
)
approved = models.BooleanField(default=False)
class Meta:
unique_together = (('askFrom', 'askTo'),)
def __str__(self):
return str(self.askFrom) + '----->' + str(self.askTo)
Message
class Message(models.Model):
message = models.CharField(max_length=140)
sender = models.ForeignKey(
settings.AUTH_USER_MODEL, related_name='sender',
on_delete=models.CASCADE
)
receiver = models.ForeignKey(
settings.AUTH_USER_MODEL, related_name='receiver',
on_delete=models.CASCADE
)
def __str__(self):
return str(self.sender)
python3 manage.py makemigrations
python3 manage.py migrate
admin.py(core)
マーキング
from django.contrib import admin
from django.contrib.auth.admin import UserAdmin as BaseUserAdmin
from django.utils.translation import gettext as _
from core import models
field
Personal Info
Permissions
class UserAdmin(BaseUserAdmin):
ordering = ['id']
list_display = ['email']
fieldsets = (
(None, {'fields': ('email', 'password')}),
(_('Personal Info'), {'fields': ('',)}),
(
_('Permissions'),
{
'fields': (
'is_active',
'is_staff',
'is_superuser',
)
}
),
(_('Important dates'), {'fields': ('last_login',)}),
)
add_fieldsets = (
(None, {
'classes':('wide',),
'fields':('email','password1','password2')
}),
)
admin.site.register(models.User, UserAdmin)
admin.site.register(models.Profile)
admin.site.register(models.Message)
admin.site.register(models.FriendRequest)
(myvenv) ~$ python3 manage.py makemigrations
(myvenv) ~$ python3 manage.py migrate
python3 manage.py createsuperuser
serializers.py
api_user>serializers.py
from django.contrib.auth import get_user_model
from rest_framework import serializers
from rest_framework.authtoken.models import Token
from core.models import Profile, FriendRequest
class UserSerializer(serializers.ModelSerializer):
class Meta:
model = get_user_model()
fields = ('id','email','password')
extra_kwargs= {'password': {'write_only': True}}
def create(self, validated_data):
user = get_user_model().objects.create_user(**validated_data)
Token.objects.create(user=user)
return user
class ProfileSerializer(serializers.ModelSerializer):
created_on = serializers.DateTimeField(format="%Y-%m-%d", read_only=True)
class Meta:
model=Profile
fields = ('id', 'nickName', 'userPro', 'created_on', 'img')
extra_kwargs = {'userPro': {'read_only': True}}
class FriendRequestSerializer(serializers.ModelSerializer):
class Meta:
model = FriendRequest
fields = ('id','askFrom','askTo','approved')
extra_kwargs = {'askFrom': {'read_only': True}}
api_dm>serializers.py
Qオブジェクトをimportしています。
Qオブジェクトについては下記記事を参照してください。
ログインしているユーザーがsenderになる。
友達申請が承認されているユーザーに対してメッセージが送れるようにFriendFilterで制限しています。
askToがログインしているユーザーかつapproved=Trueで承認されている
from rest_framework import serializers
from core.models import Message, User, Profile, FriendRequest
from django.db.models import Q
class FriendsFilter(serializers.PrimaryKeyRelatedField):
def get_queryset(self):
request = self.context['request']
friends = FriendRequest.objects.filter(Q(askTo=request.user) & Q(approved=True))
list_friend = []
for friend in friends:
list_friend.append(friend.askFrom.id)
queryset = User.objects.filter(id__in=list_friend)
return queryset
class MessageSerializer(serializers.ModelSerializer):
receiver = FriendsFilter()
class Meta:
model = Message
fields = ('id', 'sender', 'receiver', 'message')
extra_kwargs = {'sender': {'read_only': True}}
views.py
ユーザーの新規作成
class CreateUserView(generics.CreateAPIView):
serializer_class = serializers.UserSerializer