This repository has been archived by the owner on Mar 30, 2019. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 6
Backend / Event and related endpoints #95
Merged
Merged
Changes from all commits
Commits
Show all changes
21 commits
Select commit
Hold shift + click to select a range
490a16c
Create event and related models
mertakozcan 7056e15
Install `django_extensions` for `shell_plus`
mertakozcan 963adfa
Install `isort` for sorting import statements
mertakozcan ccafeeb
Revision for model fields
mertakozcan 1c52de9
Implement basic API endpoints for `Event`, `Tag`, `Comment` and `Media`
mertakozcan 5bcdde7
Remove migration files
mertakozcan 82089e1
Implement `AttendanceStatus` and generic models && Add `owner` fields
mertakozcan a56a65f
Implement a custom permission to check ownership before certain actions
mertakozcan 4ab91be
Update `serializers` and `views` to support authenticated requests
mertakozcan 6fae3c1
Make `Comment` model generic to support user and event comments
mertakozcan eafc8d4
Typo
mertakozcan 8bd2170
Implement `attendance` endpoint && Optimize view-based classes
mertakozcan 19cd9c5
Implement `follow` functionality and its API endpoint
mertakozcan 1612176
Create signals to update `follower_count`
mertakozcan d72cbe7
Implement mixins for models && Add `DELETE` for `AttendanceStatus`
mertakozcan 87adb79
General fixes && Start implementation of `vote` endpoint
mertakozcan 60baff9
Implement `vote` endpoint
mertakozcan 9c7001b
Rename
mertakozcan b307592
Fix event `create` and `update` issues && Revision for serializers
mertakozcan cd7c4c4
Inherit View classes from `views` instead of `mixins`
mertakozcan 766254a
Update urls to support `DELETE` for `attendance`, `follow` and `vote`
mertakozcan File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
default_app_config = 'api.apps.ApiConfig' |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -3,3 +3,6 @@ | |
|
||
class ApiConfig(AppConfig): | ||
name = 'api' | ||
|
||
def ready(self): | ||
import api.handlers |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
from django.contrib.contenttypes.models import ContentType | ||
from django.db.models import F | ||
from django.db.models.signals import post_save, pre_delete | ||
from django.dispatch import receiver | ||
|
||
from api.models import FollowStatus | ||
|
||
|
||
@receiver(post_save, sender=FollowStatus, dispatch_uid='increment_follower_count') | ||
def increment_follower_count(sender, instance, **kwargs): | ||
instance.content_type.model_class().objects.\ | ||
filter(id=instance.object_id).update(follower_count=F('follower_count')+1) | ||
|
||
@receiver(pre_delete, sender=FollowStatus, dispatch_uid='decrement_follower_count') | ||
def decrement_follower_count(sender, instance, **kwargs): | ||
instance.content_type.model_class().objects.\ | ||
filter(id=instance.object_id).update(follower_count=F('follower_count')-1) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,3 +1,174 @@ | ||
from django.conf import settings | ||
from django.contrib.contenttypes.fields import (GenericForeignKey, | ||
GenericRelation) | ||
from django.contrib.contenttypes.models import ContentType | ||
from django.db import models | ||
from django.utils import timezone | ||
|
||
# Create your models here. | ||
|
||
class OwnerMixin(models.Model): | ||
""" | ||
Each model that belongs to a `User` must use this mixin. | ||
""" | ||
owner = models.ForeignKey(settings.AUTH_USER_MODEL, related_name='%(class)s_set', on_delete=models.CASCADE) | ||
|
||
class Meta: | ||
abstract = True | ||
|
||
|
||
class CommentMixin(models.Model): | ||
""" | ||
Each model that contains `Comment`s must use this mixin. | ||
""" | ||
comments = GenericRelation('Comment') | ||
|
||
class Meta: | ||
abstract = True | ||
|
||
|
||
class FollowMixin(models.Model): | ||
""" | ||
Each model that can be followed by a user must use this mixin. | ||
""" | ||
followers = GenericRelation('FollowStatus') | ||
follower_count = models.IntegerField(default=0) | ||
|
||
class Meta: | ||
abstract = True | ||
|
||
|
||
class LocationMixin(models.Model): | ||
""" | ||
Each model that contains a `Location` must use this mixin. | ||
""" | ||
location = GenericRelation('Location') | ||
|
||
class Meta: | ||
abstract = True | ||
|
||
|
||
class MediaMixin(models.Model): | ||
""" | ||
Each model that contains `Media`s must use this mixin. | ||
""" | ||
medias = GenericRelation('Media') | ||
|
||
class Meta: | ||
abstract = True | ||
|
||
|
||
class VoteMixin(models.Model): | ||
""" | ||
Each model that can be voted by a user must use this mixin. | ||
""" | ||
votes = GenericRelation('VoteStatus') | ||
vote_count = models.IntegerField(default=0) | ||
|
||
class Meta: | ||
abstract = True | ||
|
||
def update_vote_count(self, vote_value, voted_before): | ||
if voted_before: | ||
vote_value = vote_value * 2 | ||
self.vote_count = self.vote_count + vote_value | ||
self.save() | ||
|
||
|
||
class GenericModelMixin(models.Model): | ||
""" | ||
Allows generic relations between different models. | ||
See https://docs.djangoproject.com/en/2.1/ref/contrib/contenttypes/ | ||
""" | ||
content_type = models.ForeignKey(ContentType, on_delete=models.CASCADE) | ||
object_id = models.PositiveIntegerField() | ||
content_object = GenericForeignKey('content_type', 'object_id') | ||
|
||
class Meta: | ||
abstract = True | ||
|
||
|
||
class Event(OwnerMixin, CommentMixin, FollowMixin, LocationMixin, MediaMixin, VoteMixin): | ||
# Related fields | ||
# TODO Decide if an artist must be a User in our system. | ||
artists = models.ManyToManyField(settings.AUTH_USER_MODEL, db_table='event_artists', | ||
related_name='performed_events', blank=True) | ||
tags = models.ManyToManyField('Tag', db_table='event_tags', related_name='events', | ||
blank=True) | ||
|
||
# Own fields | ||
title = models.CharField(max_length=100) | ||
description = models.TextField() | ||
date = models.DateTimeField(default=timezone.now) | ||
price = models.DecimalField(max_digits=6, decimal_places=2, blank=True, default=0.0) | ||
organizer_url = models.URLField(blank=True, null=True) | ||
created = models.DateTimeField(auto_now_add=True) | ||
updated = models.DateTimeField(auto_now=True) | ||
|
||
|
||
class AttendanceStatus(OwnerMixin): | ||
ATTENDANCE_STATUS = ( | ||
('Y', 'Yes'), | ||
('N', 'No'), | ||
('M', 'Maybe'), | ||
('A', 'Attended'), | ||
('B', 'Blocked'), | ||
) | ||
event = models.ForeignKey(Event, related_name='attendance_status', on_delete=models.CASCADE) | ||
status = models.CharField(max_length=1, choices=ATTENDANCE_STATUS) | ||
|
||
class Meta: | ||
# There can be only one attendance status between User and Event. | ||
unique_together = ('owner', 'event') | ||
|
||
|
||
class Comment(GenericModelMixin, OwnerMixin): | ||
content = models.TextField() | ||
created = models.DateTimeField(auto_now_add=True) | ||
updated = models.DateTimeField(auto_now=True) | ||
|
||
|
||
class FollowStatus(GenericModelMixin, OwnerMixin): | ||
class Meta: | ||
# A user cannot follow the same item more than once | ||
unique_together = ('owner', 'content_type', 'object_id') | ||
|
||
|
||
class Location(GenericModelMixin, OwnerMixin): | ||
# TODO Add required fields after doing research about Google Maps / Places API | ||
created = models.DateTimeField(auto_now_add=True) | ||
updated = models.DateTimeField(auto_now=True) | ||
|
||
class Meta: | ||
# A model (User, Event etc.) cannot have more than one Location. | ||
unique_together = ('content_type', 'object_id') | ||
|
||
|
||
class Media(GenericModelMixin, OwnerMixin): | ||
url = models.URLField() | ||
created = models.DateTimeField(auto_now_add=True) | ||
updated = models.DateTimeField(auto_now=True) | ||
|
||
|
||
class Tag(models.Model): | ||
name = models.CharField(max_length=20) | ||
|
||
|
||
class VoteStatus(GenericModelMixin, OwnerMixin): | ||
VOTE_CHOICES = ( | ||
('U', 'Up'), | ||
('D', 'Down'), | ||
) | ||
vote = models.CharField(max_length=1, choices=VOTE_CHOICES) | ||
|
||
class Meta: | ||
# A user cannot vote for the same item more than once | ||
unique_together = ('owner', 'content_type', 'object_id') | ||
|
||
@property | ||
def vote_value(self): | ||
vote_value = 0 | ||
if self.vote == 'U': | ||
vote_value = 1 | ||
elif self.vote == 'D': | ||
vote_value = -1 | ||
return vote_value |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
from rest_framework import permissions | ||
|
||
|
||
class IsOwnerOrReadOnly(permissions.BasePermission): | ||
|
||
def has_object_permission(self, request, view, obj): | ||
if request.method in permissions.SAFE_METHODS: | ||
return True | ||
return obj.owner == request.user |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe we should create a mixin for this too.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'll create a new PR later on.