Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Create user favorite beer model #197

Open
wants to merge 40 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
40 commits
Select commit Hold shift + click to select a range
3308671
Add user favorite beer model
drewbrew May 22, 2019
7de3e15
merge migrations
drewbrew Aug 22, 2019
33e3b8d
Merge branch 'master' into favorite-beer-model
drewbrew Aug 22, 2019
decddc0
Merge branch 'master' into favorite-beer-model
drewbrew Aug 23, 2019
1b8a9dc
Merge branch 'master' into favorite-beer-model
drewbrew Aug 23, 2019
76f5bf8
Merge branch 'master' into favorite-beer-model
drewbrew Aug 27, 2019
8c2d7d4
Merge branch 'master' into favorite-beer-model
drewbrew Sep 6, 2019
b172bf8
Merge branch 'master' into favorite-beer-model
drewbrew Sep 9, 2019
38f9ed9
Merge branch 'master' into favorite-beer-model
drewbrew Sep 11, 2019
f81eeb4
Merge branch 'master' into favorite-beer-model
drewbrew Oct 1, 2019
4ce08e6
Merge branch 'master' into favorite-beer-model
drewbrew Oct 16, 2019
7f39f32
Merge branch 'master' into favorite-beer-model
drewbrew Nov 15, 2019
37dde11
Fix merge migration
drewbrew Nov 15, 2019
e2e3afa
Merge branch 'master' into favorite-beer-model
drewbrew Nov 15, 2019
8b793e8
Merge branch 'master' into favorite-beer-model
drewbrew Nov 16, 2019
2bb8047
Merge branch 'master' into favorite-beer-model
drewbrew Nov 17, 2019
d499721
Merge branch 'master' into favorite-beer-model
drewbrew Nov 18, 2019
3df440f
Merge branch 'master' into favorite-beer-model
drewbrew Dec 18, 2019
3641d68
Merge branch 'master' into favorite-beer-model
drewbrew Jan 18, 2020
5593272
Merge branch 'master' into favorite-beer-model
drewbrew Mar 5, 2020
0756cf5
Merge branch 'master' into favorite-beer-model
drewbrew Jun 22, 2020
b737ade
merge migrations
drewbrew Jun 23, 2020
6e38a5b
Merge branch 'master' into favorite-beer-model
drewbrew Jun 24, 2020
2734129
Merge branch 'master' into favorite-beer-model
drewbrew Jul 22, 2020
d2b1613
Merge remote-tracking branch 'origin/master' into favorite-beer-model
drewbrew Oct 5, 2020
8235d59
run black
drewbrew Oct 5, 2020
eba1f85
Merge branch 'master' into favorite-beer-model
drewbrew Nov 20, 2020
934a6f5
Fix duplicate JSONField import
drewbrew Nov 20, 2020
4c1a939
merge migrations
drewbrew Nov 20, 2020
a869ef4
Merge branch 'master' into favorite-beer-model
drewbrew Nov 24, 2020
ad345ae
Merge branch 'master' into favorite-beer-model
drewbrew Nov 25, 2020
c8cf32d
Merge branch 'master' into favorite-beer-model
drewbrew Nov 25, 2020
b9395e1
Delete prints
drewbrew Nov 25, 2020
6d649b9
Merge branch 'master' into favorite-beer-model
drewbrew Nov 30, 2020
e8eed7d
Merge branch 'master' into favorite-beer-model
drewbrew Dec 8, 2020
1182838
Merge branch 'master' into favorite-beer-model
drewbrew Feb 20, 2021
d6757bf
Merge remote-tracking branch 'origin/master' into favorite-beer-model
drewbrew Feb 20, 2021
b8fc2fd
merge migrations
drewbrew Feb 20, 2021
dbff172
fix merge conflict resolution
drewbrew Feb 20, 2021
742b97d
Merge branch 'main' into favorite-beer-model
drewbrew May 12, 2021
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions beers/admin.py
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,14 @@ class ManufacturerAlternateNameInline(admin.TabularInline):
model = models.ManufacturerAlternateName


class UserFavoriteBeerInline(admin.TabularInline):
model = models.UserFavoriteBeer
queryset = models.UserFavoriteBeer.objects.select_related(
"user",
"beer__manufacturer",
)


class ManufacturerAdmin(admin.ModelAdmin):
def url_fields_set(self, manufacturer):
fields = [i.name for i in manufacturer._meta.fields if i.name.endswith("_url")]
Expand Down
50 changes: 50 additions & 0 deletions beers/migrations/0030_userfavoritebeer.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
# Generated by Django 2.2.1 on 2019-05-19 13:34

from django.conf import settings
from django.db import migrations, models
import django.db.models.deletion


class Migration(migrations.Migration):

dependencies = [
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
("beers", "0029_merge_20190519_1259"),
]

operations = [
migrations.CreateModel(
name="UserFavoriteBeer",
fields=[
(
"id",
models.AutoField(
auto_created=True,
primary_key=True,
serialize=False,
verbose_name="ID",
),
),
("notifications_enabled", models.BooleanField(default=False)),
(
"beer",
models.ForeignKey(
on_delete=django.db.models.deletion.CASCADE,
related_name="favored_by_users",
to="beers.Beer",
),
),
(
"user",
models.ForeignKey(
on_delete=django.db.models.deletion.CASCADE,
related_name="favorite_beers",
to=settings.AUTH_USER_MODEL,
),
),
],
options={
"unique_together": {("beer", "user")},
},
),
]
13 changes: 13 additions & 0 deletions beers/migrations/0032_merge_20191115_0316.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
# Generated by Django 2.2.7 on 2019-11-15 03:16

from django.db import migrations


class Migration(migrations.Migration):

dependencies = [
("beers", "0030_userfavoritebeer"),
("beers", "0031_beer_tweeted_about"),
]

operations = []
13 changes: 13 additions & 0 deletions beers/migrations/0035_merge_20200623_1127.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
# Generated by Django 3.0.7 on 2020-06-23 11:27

from django.db import migrations


class Migration(migrations.Migration):

dependencies = [
("beers", "0034_auto_20200602_2202"),
("beers", "0032_merge_20191115_0316"),
]

operations = []
13 changes: 13 additions & 0 deletions beers/migrations/0037_merge_20201120_1718.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
# Generated by Django 3.1.2 on 2020-11-20 17:18

from django.db import migrations


class Migration(migrations.Migration):

dependencies = [
("beers", "0035_merge_20200623_1127"),
("beers", "0036_auto_20201112_1705"),
]

operations = []
13 changes: 13 additions & 0 deletions beers/migrations/0038_merge_20210220_1507.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
# Generated by Django 3.1.7 on 2021-02-20 15:07

from django.db import migrations


class Migration(migrations.Migration):

dependencies = [
("beers", "0037_merge_20201114_1252"),
("beers", "0037_merge_20201120_1718"),
]

operations = []
20 changes: 19 additions & 1 deletion beers/models.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
import logging

from django.conf import settings
from django.contrib.postgres.fields import CITextField
from django.db import models, transaction
from django.db.models import JSONField
from django.db.utils import IntegrityError
from django.utils.timezone import now

from taps.models import Tap
from .utils import render_srm
from django.db.models import JSONField

LOG = logging.getLogger(__name__)

Expand Down Expand Up @@ -448,3 +449,20 @@ class UntappdMetadata(models.Model):
models.CASCADE,
related_name="untappd_metadata",
)


class UserFavoriteBeer(models.Model):
user = models.ForeignKey(
settings.AUTH_USER_MODEL,
models.CASCADE,
related_name="favorite_beers",
)
beer = models.ForeignKey(
Beer,
models.CASCADE,
related_name="favored_by_users",
)
notifications_enabled = models.BooleanField(default=False)

class Meta:
unique_together = (("beer", "user"),)
13 changes: 13 additions & 0 deletions beers/serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -142,3 +142,16 @@ class OtherPKSerializer(serializers.Serializer):

# we'll take care of validating during the view
id = serializers.IntegerField(min_value=0)


class UserFavoriteBeerSerializer(serializers.ModelSerializer):
class Meta:
model = models.UserFavoriteBeer
fields = "__all__"
validators = [
UniqueTogetherValidator(
fields=["user", "beer"],
queryset=models.UserFavoriteBeer.objects.all(),
message="User is already subscribed to this beer",
),
]
3 changes: 1 addition & 2 deletions hsv_dot_beer/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,13 +20,12 @@
undo_clear,
)
from venues.views import venue_table
from .users.views import UserViewSet, UserCreateViewSet
from .users.views import UserViewSet
from .views import home


router = DefaultRouter()
router.register(r"users", UserViewSet)
router.register(r"users", UserCreateViewSet)

urlpatterns = [
path("accounts/", include("django.contrib.auth.urls")),
Expand Down
4 changes: 3 additions & 1 deletion hsv_dot_beer/users/admin.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
from django.contrib import admin
from django.contrib.auth.admin import UserAdmin

from beers.admin import UserFavoriteBeerInline
from .models import User


@admin.register(User)
class UserAdmin(UserAdmin):
pass
inlines = [UserFavoriteBeerInline]
20 changes: 16 additions & 4 deletions hsv_dot_beer/users/permissions.py
Original file line number Diff line number Diff line change
@@ -1,14 +1,26 @@
from rest_framework import permissions


class IsUserOrReadOnly(permissions.BasePermission):
class UserPermission(permissions.BasePermission):
"""
Object-level permission to only allow owners of an object to edit it.
Permissions for the user model:

1. Admins can do everything
2. Normal users can only read/write themselves
"""

def has_permission(self, request, view):
if request.user.is_staff:
return True
if request.method == "POST" and "subscribe" in request.path:
return True
return request.method in permissions.SAFE_METHODS + ("PUT", "PATCH")

def has_object_permission(self, request, view, obj):

if request.method in permissions.SAFE_METHODS:
if request.user == obj:
return True
if request.user.is_staff:
return True

return obj == request.user
return request.method in permissions.SAFE_METHODS
69 changes: 69 additions & 0 deletions hsv_dot_beer/users/test/test_views.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@
from rest_framework.test import APITestCase
from rest_framework import status
from faker import Faker

from beers.test.factories import BeerFactory
from beers.models import UserFavoriteBeer
from ..models import User
from .factories import UserFactory

Expand Down Expand Up @@ -44,6 +47,7 @@ def test_post_request_with_valid_data_succeeds(self):
ok_(check_password(self.user_data.get("password"), user.password))

def test_post_request_unauthorized(self):
self.client.credentials(HTTP_AUTHORIZATION="")
response = self.client.post(
self.url,
json.dumps(self.user_data),
Expand All @@ -63,14 +67,79 @@ def setUp(self):
self.client.credentials(HTTP_AUTHORIZATION=f"Token {self.user.auth_token}")

def test_get_request_returns_a_given_user(self):

response = self.client.get(self.url)
eq_(response.status_code, status.HTTP_200_OK)

def test_put_request_updates_a_user(self):
self.client.credentials(
HTTP_AUTHORIZATION=f"Token {self.user.auth_token}",
)

new_first_name = fake.first_name()
payload = {"first_name": new_first_name}
response = self.client.put(self.url, payload)
eq_(response.status_code, status.HTTP_200_OK)

user = User.objects.get(pk=self.user.id)
eq_(user.first_name, new_first_name)

def test_subscribe_to_beer(self):
beer = BeerFactory()
url = reverse("user-subscribetobeer", kwargs={"pk": self.user.pk})
print(url)
payload = {
"beer": beer.id,
"notifications_enabled": True,
}
self.client.credentials(
HTTP_AUTHORIZATION=f"Token {self.user.auth_token}",
)

response = self.client.post(url, payload)
eq_(response.status_code, status.HTTP_200_OK, response.data)
eq_(UserFavoriteBeer.objects.count(), 1)

def test_update_subscription_to_beer(self):
beer = BeerFactory()
sub = UserFavoriteBeer.objects.create(
beer=beer,
user=self.user,
notifications_enabled=True,
)
url = reverse("user-subscribetobeer", kwargs={"pk": self.user.pk})

payload = {
"beer": beer.id,
"notifications_enabled": False,
}
self.client.credentials(
HTTP_AUTHORIZATION=f"Token {self.user.auth_token}",
)

response = self.client.post(url, payload)
eq_(response.status_code, status.HTTP_200_OK, response.content)
eq_(UserFavoriteBeer.objects.count(), 1)
sub.refresh_from_db()
self.assertFalse(sub.notifications_enabled)

def test_unsubscribe_from_beer(self):
beer = BeerFactory()
UserFavoriteBeer.objects.create(
beer=beer,
user=self.user,
notifications_enabled=True,
)
url = reverse("user-unsubscribefrombeer", kwargs={"pk": self.user.pk})

payload = {
"beer": beer.id,
"notifications_enabled": False,
}
self.client.credentials(
HTTP_AUTHORIZATION=f"Token {self.user.auth_token}",
)

response = self.client.post(url, payload)
eq_(response.status_code, status.HTTP_204_NO_CONTENT, response.content)
eq_(UserFavoriteBeer.objects.count(), 0)
Loading