Skip to content

Commit

Permalink
Merge pull request #49 from MomsPops/friends_michael
Browse files Browse the repository at this point in the history
Accounts friends michael
  • Loading branch information
chertegnic committed Aug 7, 2023
2 parents 6298283 + 17e4b31 commit 6862ce0
Show file tree
Hide file tree
Showing 18 changed files with 707 additions and 48 deletions.
6 changes: 4 additions & 2 deletions src/locations/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ class City(models.Model):
"""
City model.
"""
name = models.CharField(max_length=100)
name = models.CharField(max_length=100, db_index=True)
region = models.ForeignKey(
to="Region",
on_delete=models.CASCADE,
Expand All @@ -41,6 +41,7 @@ def __str__(self):
return self.name

class Meta:
ordering = ['name']
unique_together = ("name", "region")
verbose_name = "Город"
verbose_name_plural = "Города"
Expand All @@ -50,7 +51,7 @@ class Region(models.Model):
"""
Region model.
"""
name = models.CharField(max_length=100, unique=True)
name = models.CharField(max_length=100, unique=True, db_index=True)

objects = models.Manager()
region_manager = RegionManager()
Expand All @@ -59,5 +60,6 @@ def __str__(self):
return self.name

class Meta:
ordering = ['name']
verbose_name = "Регион"
verbose_name_plural = "Регионы"
6 changes: 4 additions & 2 deletions src/profiles/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,15 @@ def all(self):
return (
super()
.select_related("account", "account__user")
.prefetch_related("account__friends")
.all()
)

def get(self, *args, **kwargs):
return (
super()
.select_related("account", "account__user")
.select_related("account", "account__user", "account__coordinate")
.prefetch_related("account__friends")
.get(*args, **kwargs)
)

Expand All @@ -41,7 +43,7 @@ class Profile(UUIDModel, AccountOneToOneModel): # type: ignore
tags = models.ManyToManyField("Tag", verbose_name="profiles", related_name="tags", blank=True)
sex = models.CharField("Пол", choices=SEX_CHOICES, default="Не выбран", max_length=10)

objects = models.Manager()
objects = ProfileManager()
profile_manager = ProfileManager()

def get_photo_url(self) -> str:
Expand Down
92 changes: 92 additions & 0 deletions src/profiles/tests/test_view.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
from http import HTTPStatus

from django.urls import reverse
from rest_framework.test import APITestCase

from profiles.models import Post, Profile
from service.fixtues import TestProfileFixture, TestPostFixture
from users.models import Account


class TestProfileViews(TestProfileFixture, APITestCase):
Expand Down Expand Up @@ -47,6 +50,95 @@ def test_profile_posts(self):
)
self.assertEqual(response.status_code, 200)

def test_profile_friends_both_side(self):
response = self.user2_client.get(
reverse("profiles_friends", kwargs={'username': self.superuser_account.user.username})
)
self.assertEqual(response.status_code, HTTPStatus.OK)
data = response.json()
self.assertEqual(len(data), 1)
account3_data = data[0]
self.assertEqual(account3_data['user']['username'], self.user3_account.user.username)

def test_profile_friends(self):
response = self.user_client.get(
reverse("profiles_friends", kwargs={'username': self.user3_account.user.username})
)
self.assertEqual(response.status_code, HTTPStatus.OK)
data = response.json()
self.assertEqual(len(data), 1)
account3_data = data[0]
self.assertEqual(account3_data['city_name'], self.superuser_account.city.name)

def test_profile_fail_unauthorized(self):
response = self.client.get(
reverse("profiles_friends", kwargs={'username': self.user3_account.user.username})
)
self.assertEqual(response.status_code, HTTPStatus.UNAUTHORIZED)
data = response.json()
self.assertIn('detail', data)

def test_profile_friend_delete_fail(self):
account1_friends_amount_before = len(self.user_account.friends.all())
all_accounts_friends_amount_before = sum(a.friends.count() for a in Account.objects.all())
response = self.user_client.delete(
reverse("profiles_friend_delete", kwargs={'username': self.user3_account.user.username})
)
self.assertEqual(response.status_code, HTTPStatus.BAD_REQUEST)
data = response.json()
self.assertIn('detail', data)
self.assertEqual(data['detail'], f"Account {self.user3_account} is not your friend.")
self.user_account.refresh_from_db()
account1_friends_after = self.user_account.friends.all()
all_accounts_friends_amount_after = sum(a.friends.count() for a in Account.objects.all())
self.assertEqual(len(account1_friends_after), account1_friends_amount_before)
self.assertEqual(all_accounts_friends_amount_after, all_accounts_friends_amount_before)

def test_profile_friend_fail_yourself(self):
account1_friends_amount_before = len(self.user_account.friends.all())
all_accounts_friends_amount_before = sum(a.friends.count() for a in Account.objects.all())
response = self.user_client.delete(
reverse("profiles_friend_delete", kwargs={'username': self.user_account.user.username})
)
self.assertEqual(response.status_code, HTTPStatus.BAD_REQUEST)
data = response.json()
self.assertIn('detail', data)
self.assertEqual(data['detail'], "Cannot delete yourself from friends.")
self.user_account.refresh_from_db()
account1_friends_after = self.user_account.friends.all()
all_accounts_friends_amount_after = sum(a.friends.count() for a in Account.objects.all())
self.assertEqual(len(account1_friends_after), account1_friends_amount_before)
self.assertEqual(all_accounts_friends_amount_after, all_accounts_friends_amount_before)

def test_profile_friend_fail_unauthorized(self):
all_accounts_friends_amount_before = sum(a.friends.count() for a in Account.objects.all())
response = self.client.delete(
reverse("profiles_friend_delete", kwargs={'username': self.user2_account.user.username})
)
self.assertEqual(response.status_code, HTTPStatus.UNAUTHORIZED)
data = response.json()
self.assertIn('detail', data)
self.user_account.refresh_from_db()
all_accounts_friends_amount_after = sum(a.friends.count() for a in Account.objects.all())
self.assertEqual(all_accounts_friends_amount_after, all_accounts_friends_amount_before)

def test_profile_friend_success(self):
self.assertIn(self.user3_account, self.superuser_account.friends.all())
self.assertIn(self.superuser_account, self.user3_account.friends.all())
all_accounts_friends_amount_before = sum(a.friends.count() for a in Account.objects.all())
response = self.user3_client.delete(
reverse("profiles_friend_delete", kwargs={'username': self.superuser_account.user.username})
)
self.assertEqual(response.status_code, HTTPStatus.OK)
data = response.json()
self.assertIn('detail', data)
self.assertEqual(data['detail'], "Friendship is broken successfully.")
self.user_account.refresh_from_db()
all_accounts_friends_amount_after = sum(a.friends.count() for a in Account.objects.all())
self.assertEqual(all_accounts_friends_amount_before - all_accounts_friends_amount_after, 2)
self.assertNotIn(self.user3_account, self.superuser_account.friends.all())
self.assertNotIn(self.superuser_account, self.user3_account.friends.all())


class TestPostView(TestPostFixture, APITestCase):

Expand Down
52 changes: 45 additions & 7 deletions src/profiles/views.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,17 @@
from http import HTTPStatus

from rest_framework import viewsets, mixins
from rest_framework.permissions import IsAuthenticated
from rest_framework.decorators import action
from rest_framework.response import Response

from users.models import Account
from .models import Profile, Post
from .serializers import (
ProfileListSerializer, ProfileDetailSerializer,
PostDetailSerializer, PostListSerializer, PostCreateSerializer
)
from users.serializers import AccountDetailSerializer
from .permissions import IsProfileOwner, IsPostOwner


Expand All @@ -20,19 +24,31 @@ class ProfileViewSet(mixins.ListModelMixin,
lookup_url_kwarg = "username"
lookup_field = "account__user__username"

def retrieve(self, request, *args, **kwargs):
profile = self.get_object()
serializer = self.get_serializer(profile)
data = serializer.data
data['is_friend'] = profile.account in request.user.account.friends.all()
if not data["is_friend"]:
data['is_outcoming'] = request.user.account.outcoming_requests.filter(to_account=profile.account).exists()
data['is_incoming'] = request.user.account.incoming_requests.filter(to_account=profile.account).exists()
return Response(data)

def get_serializer(self, *args, **kwargs):
if self.action == "list":
return ProfileListSerializer(*args, **kwargs)
serializer = ProfileListSerializer
elif self.action == "posts":
return PostListSerializer(*args, **kwargs)
serializer = PostListSerializer
elif self.action == "friends":
serializer = AccountDetailSerializer
else:
return ProfileDetailSerializer(*args, **kwargs)
serializer = ProfileDetailSerializer
return serializer(*args, **kwargs)

def get_permissions(self):
if self.action in ("list", "retrieve", "posts"):
permission_classes = [IsAuthenticated]
else:
permission_classes = [IsProfileOwner]
permission_classes = [IsAuthenticated]
if self.action in ("partial_update", "update", "friends_delete"):
permission_classes.append(IsProfileOwner)
return [pc() for pc in permission_classes]

def update(self, request, *args, **kwargs):
Expand All @@ -51,6 +67,28 @@ def posts(self, request, username):
serializer = self.get_serializer(instance=posts, many=True)
return Response(serializer.data)

@action(methods=['get'], detail=True)
def friends(self, request, username):
profile = self.get_object()
serializer = self.get_serializer(instance=profile.account.friends.all(), many=True)
return Response(serializer.data)

@action(methods=['delete'], detail=True, url_path='friends-delete', url_name='friend_delete')
def friend_delete(self, request, username):
profile = self.get_object()
if profile.account == request.user.account:
return Response(
{"detail": "Cannot delete yourself from friends."},
status=HTTPStatus.BAD_REQUEST
)
elif profile.account not in request.user.account.friends.all():
return Response(
{"detail": f"Account {profile.account} is not your friend."},
status=HTTPStatus.BAD_REQUEST
)
Account.objects.break_friendship(profile.account, request.user.account)
return Response({"detail": "Friendship is broken successfully."})


class PostViewSet(mixins.RetrieveModelMixin,
mixins.CreateModelMixin,
Expand Down
18 changes: 17 additions & 1 deletion src/service/fixtues.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
from locations.models import Region, City
from notifications.models import NotificationAccount, Notification
from profiles.models import Profile, Post
from users.models import Account, User
from users.models import Account, User, FriendshipRequest
from chats.models import Group, GroupMessage


Expand Down Expand Up @@ -61,6 +61,22 @@ def setUpClass(cls):

cls.user2_account = Account.objects.create(user=cls.user2)
cls.user3_account = Account.objects.create(user=cls.user3)
cls.superuser_account.friends.add(cls.user3_account)


class TestFriendshipRequestFixture(TestAccountFixture, APITestCase):

@classmethod
def setUpClass(cls):
super().setUpClass()
cls.friendship_request_from_1_to_2 = FriendshipRequest.friendship_request_manager.create_friendship_request(
from_account=cls.user_account,
to_account=cls.user2_account
)
cls.friendship_request_from_3_to_2 = FriendshipRequest.friendship_request_manager.create_friendship_request(
from_account=cls.user3_account,
to_account=cls.user2_account
)


class TestProfileFixture(TestAccountFixture, APITestCase):
Expand Down
7 changes: 6 additions & 1 deletion src/users/admin.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
from django.contrib import admin

from .models import Account, User
from .models import Account, User, FriendshipRequest


@admin.register(Account)
Expand All @@ -13,3 +13,8 @@ class AccountAdmin(admin.ModelAdmin):
@admin.register(User)
class UserAdmin(admin.ModelAdmin):
list_display = ("id", "username", "email")


@admin.register(FriendshipRequest)
class FriendshipRequestAdmin(admin.ModelAdmin):
list_display = ("id", "to_account", "from_account")
20 changes: 20 additions & 0 deletions src/users/data/accounts.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
[
{
"user": {
"username": "michael7nightingale",
"password": "password",
"first_name": "Michael",
"last_name": "Nightingale",
"email": "nightingalemichael820@gmail.com"
}
},
{
"user": {
"username": "suslanmopl",
"password": "password",
"first_name": "Suslan",
"last_name": "Mopl",
"email": "suslanchikmopl@gmail.com"
}
}
]
Empty file.
Empty file.
18 changes: 18 additions & 0 deletions src/users/management/commands/create_accounts.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
from django.core.management.base import BaseCommand
from django.db import IntegrityError
import json

from users.models import Account


class Command(BaseCommand):
help = "Creates account with users and profiles from test data."

def handle(self, *args, **options):
with open("users/data/accounts.json") as file:
for account_data in json.load(file):
try:
account = Account.objects.create_account(**account_data)
Account.objects.activate(account)
except IntegrityError:
print(f"Account {account_data['user']['username']} already exists.")
Loading

0 comments on commit 6862ce0

Please sign in to comment.