Commit be7810aad94925b3229db6ffa70e1b2c6e759c59

Authored by Laura Hawkins
1 parent 8fa22f31e3
Exists in master

working on adding a class to a user

Showing 4 changed files with 18 additions and 12 deletions Inline Diff

flashcards/models.py View file @ be7810a
from django.contrib.auth.models import AbstractBaseUser, PermissionsMixin, AbstractUser, UserManager 1 1 from django.contrib.auth.models import AbstractBaseUser, PermissionsMixin, AbstractUser, UserManager
from django.db.models import * 2 2 from django.db.models import *
from django.utils.timezone import now 3 3 from django.utils.timezone import now
from simple_email_confirmation import SimpleEmailConfirmationUserMixin 4 4 from simple_email_confirmation import SimpleEmailConfirmationUserMixin
5 5
# Hack to fix AbstractUser before subclassing it 6 6 # Hack to fix AbstractUser before subclassing it
AbstractUser._meta.get_field('email')._unique = True 7 7 AbstractUser._meta.get_field('email')._unique = True
AbstractUser._meta.get_field('username')._unique = False 8 8 AbstractUser._meta.get_field('username')._unique = False
9 9
10 10
class EmailOnlyUserManager(UserManager): 11 11 class EmailOnlyUserManager(UserManager):
""" 12 12 """
A tiny extension of Django's UserManager which correctly creates users 13 13 A tiny extension of Django's UserManager which correctly creates users
without usernames (using emails instead). 14 14 without usernames (using emails instead).
""" 15 15 """
16 16
def _create_user(self, email, password, is_staff, is_superuser, **extra_fields): 17 17 def _create_user(self, email, password, is_staff, is_superuser, **extra_fields):
""" 18 18 """
Creates and saves a User with the given email and password. 19 19 Creates and saves a User with the given email and password.
""" 20 20 """
email = self.normalize_email(email) 21 21 email = self.normalize_email(email)
user = self.model(email=email, 22 22 user = self.model(email=email,
is_staff=is_staff, is_active=True, 23 23 is_staff=is_staff, is_active=True,
is_superuser=is_superuser, 24 24 is_superuser=is_superuser,
date_joined=now(), **extra_fields) 25 25 date_joined=now(), **extra_fields)
user.set_password(password) 26 26 user.set_password(password)
user.save(using=self._db) 27 27 user.save(using=self._db)
return user 28 28 return user
29 29
def create_user(self, email, password=None, **extra_fields): 30 30 def create_user(self, email, password=None, **extra_fields):
return self._create_user(email, password, False, False, **extra_fields) 31 31 return self._create_user(email, password, False, False, **extra_fields)
32 32
def create_superuser(self, email, password, **extra_fields): 33 33 def create_superuser(self, email, password, **extra_fields):
return self._create_user(email, password, True, True, **extra_fields) 34 34 return self._create_user(email, password, True, True, **extra_fields)
35 35
36 36
class User(AbstractUser, SimpleEmailConfirmationUserMixin): 37 37 class User(AbstractUser, SimpleEmailConfirmationUserMixin):
""" 38 38 """
An extension of Django's default user model. 39 39 An extension of Django's default user model.
We use email as the username field, and include enrolled sections here 40 40 We use email as the username field, and include enrolled sections here
""" 41 41 """
objects = EmailOnlyUserManager() 42 42 objects = EmailOnlyUserManager()
USERNAME_FIELD = 'email' 43 43 USERNAME_FIELD = 'email'
REQUIRED_FIELDS = [] 44 44 REQUIRED_FIELDS = []
sections = ManyToManyField('Section', help_text="The sections which the user is enrolled in") 45 45 sections = ManyToManyField('Section', help_text="The sections which the user is enrolled in")
46 46
47 47
class UserFlashcard(Model): 48 48 class UserFlashcard(Model):
""" 49 49 """
Represents the relationship between a user and a flashcard by: 50 50 Represents the relationship between a user and a flashcard by:
1. A user has a flashcard in their deck 51 51 1. A user has a flashcard in their deck
2. A user used to have a flashcard in their deck 52 52 2. A user used to have a flashcard in their deck
3. A user has a flashcard hidden from them 53 53 3. A user has a flashcard hidden from them
""" 54 54 """
user = ForeignKey('User') 55 55 user = ForeignKey('User')
mask = ForeignKey('FlashcardMask', help_text="A mask which overrides the card's mask") 56 56 mask = ForeignKey('FlashcardMask', help_text="A mask which overrides the card's mask")
pulled = DateTimeField(blank=True, null=True, help_text="When the user pulled the card") 57 57 pulled = DateTimeField(blank=True, null=True, help_text="When the user pulled the card")
flashcard = ForeignKey('Flashcard') 58 58 flashcard = ForeignKey('Flashcard')
unpulled = DateTimeField(blank=True, null=True, help_text="When the user unpulled this card") 59 59 unpulled = DateTimeField(blank=True, null=True, help_text="When the user unpulled this card")
60 60
class Meta: 61 61 class Meta:
# There can be at most one UserFlashcard for each User and Flashcard 62 62 # There can be at most one UserFlashcard for each User and Flashcard
unique_together = (('user', 'flashcard'),) 63 63 unique_together = (('user', 'flashcard'),)
index_together = ["user", "flashcard"] 64 64 index_together = ["user", "flashcard"]
# By default, order by most recently pulled 65 65 # By default, order by most recently pulled
ordering = ['-pulled'] 66 66 ordering = ['-pulled']
67 67
def is_hidden(self): 68 68 def is_hidden(self):
""" 69 69 """
A card is hidden only if a user has not ever added it to their deck. 70 70 A card is hidden only if a user has not ever added it to their deck.
:return: Whether the flashcard is hidden from the user 71 71 :return: Whether the flashcard is hidden from the user
""" 72 72 """
return not self.pulled 73 73 return not self.pulled
74 74
def is_in_deck(self): 75 75 def is_in_deck(self):
""" 76 76 """
:return:Whether the flashcard is in the user's deck 77 77 :return:Whether the flashcard is in the user's deck
""" 78 78 """
return self.pulled and not self.unpulled 79 79 return self.pulled and not self.unpulled
80 80
81 81
class FlashcardMask(Model): 82 82 class FlashcardMask(Model):
""" 83 83 """
A serialized list of character ranges that can be blanked out during review. 84 84 A serialized list of character ranges that can be blanked out during review.
This is encoded as '13-145,150-195' 85 85 This is encoded as '13-145,150-195'
""" 86 86 """
ranges = CharField(max_length=255) 87 87 ranges = CharField(max_length=255)
88 88
89 89
class Flashcard(Model): 90 90 class Flashcard(Model):
text = CharField(max_length=255, help_text='The text on the card') 91 91 text = CharField(max_length=255, help_text='The text on the card')
section = ForeignKey('Section', help_text='The section with which the card is associated') 92 92 section = ForeignKey('Section', help_text='The section with which the card is associated')
pushed = DateTimeField(auto_now_add=True, help_text="When the card was first pushed") 93 93 pushed = DateTimeField(auto_now_add=True, help_text="When the card was first pushed")
material_date = DateTimeField(help_text="The date with which the card is associated") 94 94 material_date = DateTimeField(help_text="The date with which the card is associated")
previous = ForeignKey('Flashcard', null=True, blank=True, 95 95 previous = ForeignKey('Flashcard', null=True, blank=True,
help_text="The previous version of this card, if one exists") 96 96 help_text="The previous version of this card, if one exists")
author = ForeignKey(User) 97 97 author = ForeignKey(User)
is_hidden = BooleanField(default=False) 98 98 is_hidden = BooleanField(default=False)
hide_reason = CharField(blank=True, max_length=255, help_text="Reason for hiding this card") 99 99 hide_reason = CharField(blank=True, max_length=255, help_text="Reason for hiding this card")
mask = ForeignKey(FlashcardMask, blank=True, null=True, help_text="The default mask for this card") 100 100 mask = ForeignKey(FlashcardMask, blank=True, null=True, help_text="The default mask for this card")
101 101
class Meta: 102 102 class Meta:
# By default, order by most recently pushed 103 103 # By default, order by most recently pushed
ordering = ['-pushed'] 104 104 ordering = ['-pushed']
105 105
def is_hidden_from(self, user): 106 106 def is_hidden_from(self, user):
""" 107 107 """
A card can be hidden globally, but if a user has the card in their deck, 108 108 A card can be hidden globally, but if a user has the card in their deck,
this visibility overrides a global hide. 109 109 this visibility overrides a global hide.
:param user: 110 110 :param user:
:return: Whether the card is hidden from the user. 111 111 :return: Whether the card is hidden from the user.
""" 112 112 """
result = user.userflashcard_set.filter(flashcard=self) 113 113 result = user.userflashcard_set.filter(flashcard=self)
if not result.exists(): return self.is_hidden 114 114 if not result.exists(): return self.is_hidden
return result[0].is_hidden() 115 115 return result[0].is_hidden()
116 116
117 117
@classmethod 118 118 @classmethod
def cards_visible_to(cls, user): 119 119 def cards_visible_to(cls, user):
""" 120 120 """
:param user: 121 121 :param user:
:return: A queryset with all cards that should be visible to a user. 122 122 :return: A queryset with all cards that should be visible to a user.
""" 123 123 """
return cls.objects.filter(hidden=False).exclude(userflashcard=user, userflashcard__pulled=None) 124 124 return cls.objects.filter(hidden=False).exclude(userflashcard=user, userflashcard__pulled=None)
125 125
126 126
class UserFlashcardReview(Model): 127 127 class UserFlashcardReview(Model):
""" 128 128 """
An event of a user reviewing a flashcard. 129 129 An event of a user reviewing a flashcard.
""" 130 130 """
user_flashcard = ForeignKey(UserFlashcard) 131 131 user_flashcard = ForeignKey(UserFlashcard)
when = DateTimeField() 132 132 when = DateTimeField()
blanked_word = CharField(max_length=8, blank=True, help_text="The character range which was blanked") 133 133 blanked_word = CharField(max_length=8, blank=True, help_text="The character range which was blanked")
response = CharField(max_length=255, blank=True, null=True, help_text="The user's response") 134 134 response = CharField(max_length=255, blank=True, null=True, help_text="The user's response")
correct = NullBooleanField(help_text="The user's self-evaluation of their response") 135 135 correct = NullBooleanField(help_text="The user's self-evaluation of their response")
136 136
def status(self): 137 137 def status(self):
""" 138 138 """
There are three stages of a review object: 139 139 There are three stages of a review object:
1. the user has been shown the card 140 140 1. the user has been shown the card
2. the user has answered the card 141 141 2. the user has answered the card
3. the user has self-evaluated their response's correctness 142 142 3. the user has self-evaluated their response's correctness
143 143
:return: string (evaluated, answered, viewed) 144 144 :return: string (evaluated, answered, viewed)
""" 145 145 """
if self.correct is not None: return "evaluated" 146 146 if self.correct is not None: return "evaluated"
if self.response: return "answered" 147 147 if self.response: return "answered"
return "viewed" 148 148 return "viewed"
149 149
150 150
class Section(Model): 151 151 class Section(Model):
""" 152 152 """
A UCSD course taught by an instructor during a quarter. 153 153 A UCSD course taught by an instructor during a quarter.
Different sections taught by the same instructor in the same quarter are considered identical. 154 154 Different sections taught by the same instructor in the same quarter are considered identical.
We use the term "section" to avoid collision with the builtin keyword "class" 155 155 We use the term "section" to avoid collision with the builtin keyword "class"
""" 156 156 """
department = CharField(max_length=50) 157 157 department = CharField(max_length=50)
course_num = CharField(max_length=6) 158 158 course_num = CharField(max_length=6)
# section_id = CharField(max_length=10) 159 159 # section_id = CharField(max_length=10)
course_title = CharField(max_length=50) 160 160 course_title = CharField(max_length=50)
instructor = CharField(max_length=100) 161 161 instructor = CharField(max_length=100)
quarter = CharField(max_length=4) 162 162 quarter = CharField(max_length=4)
whitelist = ManyToManyField(User, related_name="whitelisted_sections") 163 163 whitelist = ManyToManyField(User, related_name="whitelisted_sections")
164 164
flashcards/views.py View file @ be7810a
from flashcards.api import StandardResultsSetPagination 1 1 from flashcards.api import StandardResultsSetPagination
from flashcards.models import Section, User 2 2 from flashcards.models import Section, User
from flashcards.serializers import SectionSerializer, UserUpdateSerializer, RegistrationSerializer, UserSerializer, \ 3 3 from flashcards.serializers import SectionSerializer, UserUpdateSerializer, RegistrationSerializer, UserSerializer, \
PasswordResetSerializer, PasswordResetRequestSerializer, EmailPasswordSerializer 4 4 PasswordResetSerializer, PasswordResetRequestSerializer, EmailPasswordSerializer
from rest_framework.permissions import IsAuthenticated 5 5 from rest_framework.permissions import IsAuthenticated
from rest_framework.viewsets import ReadOnlyModelViewSet 6 6 from rest_framework.viewsets import ReadOnlyModelViewSet, ModelViewSet
from django.core.mail import send_mail 7 7 from django.core.mail import send_mail
from django.contrib.auth import authenticate, login, logout 8 8 from django.contrib.auth import authenticate, login, logout
from django.contrib.auth.tokens import default_token_generator 9 9 from django.contrib.auth.tokens import default_token_generator
from rest_framework.status import HTTP_204_NO_CONTENT, HTTP_401_UNAUTHORIZED, HTTP_201_CREATED 10 10 from rest_framework.status import HTTP_204_NO_CONTENT, HTTP_401_UNAUTHORIZED, HTTP_201_CREATED
from rest_framework.views import APIView 11 11 from rest_framework.views import APIView
from rest_framework.response import Response 12 12 from rest_framework.response import Response
from rest_framework.exceptions import AuthenticationFailed, NotAuthenticated, ValidationError 13 13 from rest_framework.exceptions import AuthenticationFailed, NotAuthenticated, ValidationError
from simple_email_confirmation import EmailAddress 14 14 from simple_email_confirmation import EmailAddress
15 from rest_framework import filters
15 16
16
class SectionViewSet(ReadOnlyModelViewSet): 17 17 class SectionViewSet(ReadOnlyModelViewSet):
queryset = Section.objects.all() 18 18 queryset = Section.objects.all()
serializer_class = SectionSerializer 19 19 serializer_class = SectionSerializer
pagination_class = StandardResultsSetPagination 20 20 pagination_class = StandardResultsSetPagination
21 21
class UserSectionViewSet(ModelViewSet): 22 22 class UserSectionViewSet(ModelViewSet):
def list(self, request, format=None): 23 23 serializer_class = SectionSerializer
queryset = User.sections.all() 24 24 permission_classes = [IsAuthenticated]
serializer_class = SectionSerializer 25 25 def get_queryset(self):
return Response(serializer.data) 26 26 return self.request.user.sections.all()
def get_queryset( 27 27
28 def paginate_queryset(self, queryset): return None
28 29
class UserDetail(APIView): 29 30 class UserDetail(APIView):
def patch(self, request, format=None): 30 31 def patch(self, request, format=None):
""" 31 32 """
Updates the user's password, or verifies their email address 32 33 Updates the user's password, or verifies their email address
--- 33 34 ---
request_serializer: UserUpdateSerializer 34 35 request_serializer: UserUpdateSerializer
response_serializer: UserSerializer 35 36 response_serializer: UserSerializer
""" 36 37 """
data = UserUpdateSerializer(data=request.data, context={'user': request.user}) 37 38 data = UserUpdateSerializer(data=request.data, context={'user': request.user})
data.is_valid(raise_exception=True) 38 39 data.is_valid(raise_exception=True)
data = data.validated_data 39 40 data = data.validated_data
40 41
if 'new_password' in data: 41 42 if 'new_password' in data:
if not request.user.is_authenticated(): 42 43 if not request.user.is_authenticated():
raise NotAuthenticated('You must be logged in to change your password') 43 44 raise NotAuthenticated('You must be logged in to change your password')
if not request.user.check_password(data['old_password']): 44 45 if not request.user.check_password(data['old_password']):
raise ValidationError('old_password is incorrect') 45 46 raise ValidationError('old_password is incorrect')
request.user.set_password(data['new_password']) 46 47 request.user.set_password(data['new_password'])
request.user.save() 47 48 request.user.save()
48 49
if 'confirmation_key' in data: 49 50 if 'confirmation_key' in data:
try: 50 51 try:
request.user.confirm_email(data['confirmation_key']) 51 52 request.user.confirm_email(data['confirmation_key'])
except EmailAddress.DoesNotExist: 52 53 except EmailAddress.DoesNotExist:
raise ValidationError('confirmation_key is invalid') 53 54 raise ValidationError('confirmation_key is invalid')
54 55
return Response(UserSerializer(request.user).data) 55 56 return Response(UserSerializer(request.user).data)
56 57
def get(self, request, format=None): 57 58 def get(self, request, format=None):
""" 58 59 """
Return data about the user 59 60 Return data about the user
--- 60 61 ---
response_serializer: UserSerializer 61 62 response_serializer: UserSerializer
""" 62 63 """
if not request.user.is_authenticated(): return Response(status=HTTP_401_UNAUTHORIZED) 63 64 if not request.user.is_authenticated(): return Response(status=HTTP_401_UNAUTHORIZED)
serializer = UserSerializer(request.user) 64 65 serializer = UserSerializer(request.user)
return Response(serializer.data) 65 66 return Response(serializer.data)
66 67
def post(self, request, format=None): 67 68 def post(self, request, format=None):
""" 68 69 """
Register a new user 69 70 Register a new user
--- 70 71 ---
request_serializer: EmailPasswordSerializer 71 72 request_serializer: EmailPasswordSerializer
response_serializer: UserSerializer 72 73 response_serializer: UserSerializer
""" 73 74 """
data = RegistrationSerializer(data=request.data) 74 75 data = RegistrationSerializer(data=request.data)
data.is_valid(raise_exception=True) 75 76 data.is_valid(raise_exception=True)
76 77
User.objects.create_user(**data.validated_data) 77 78 User.objects.create_user(**data.validated_data)
user = authenticate(**data.validated_data) 78 79 user = authenticate(**data.validated_data)
login(request, user) 79 80 login(request, user)
80 81
body = ''' 81 82 body = '''
Visit the following link to confirm your email address: 82 83 Visit the following link to confirm your email address:
https://flashy.cards/app/verify_email/%s 83 84 https://flashy.cards/app/verify_email/%s
84 85
If you did not register for Flashy, no action is required. 85 86 If you did not register for Flashy, no action is required.
''' 86 87 '''
87 88
assert send_mail("Flashy email verification", 88 89 assert send_mail("Flashy email verification",
body % user.confirmation_key, 89 90 body % user.confirmation_key,
"noreply@flashy.cards", 90 91 "noreply@flashy.cards",
[user.email]) 91 92 [user.email])
92 93
return Response(UserSerializer(request.user).data, status=HTTP_201_CREATED) 93 94 return Response(UserSerializer(request.user).data, status=HTTP_201_CREATED)
94 95
def delete(self, request): 95 96 def delete(self, request):
""" 96 97 """
Irrevocably delete the user and their data 97 98 Irrevocably delete the user and their data
98 99
Yes, really 99 100 Yes, really
""" 100 101 """
request.user.delete() 101 102 request.user.delete()
return Response(status=HTTP_204_NO_CONTENT) 102 103 return Response(status=HTTP_204_NO_CONTENT)
103 104
104 105
class UserLogin(APIView): 105 106 class UserLogin(APIView):
def post(self, request): 106 107 def post(self, request):
""" 107 108 """
Authenticates user and returns user data if valid. 108 109 Authenticates user and returns user data if valid.
--- 109 110 ---
request_serializer: EmailPasswordSerializer 110 111 request_serializer: EmailPasswordSerializer
response_serializer: UserSerializer 111 112 response_serializer: UserSerializer
""" 112 113 """
113 114
data = EmailPasswordSerializer(data=request.data) 114 115 data = EmailPasswordSerializer(data=request.data)
data.is_valid(raise_exception=True) 115 116 data.is_valid(raise_exception=True)
user = authenticate(**data.validated_data) 116 117 user = authenticate(**data.validated_data)
117 118
if user is None: 118 119 if user is None:
raise AuthenticationFailed('Invalid email or password') 119 120 raise AuthenticationFailed('Invalid email or password')
if not user.is_active: 120 121 if not user.is_active:
raise NotAuthenticated('Account is disabled') 121 122 raise NotAuthenticated('Account is disabled')
login(request, user) 122 123 login(request, user)
return Response(UserSerializer(request.user).data) 123 124 return Response(UserSerializer(request.user).data)
124 125
125 126
class UserLogout(APIView): 126 127 class UserLogout(APIView):
permission_classes = (IsAuthenticated,) 127 128 permission_classes = (IsAuthenticated,)
def post(self, request, format=None): 128 129 def post(self, request, format=None):
""" 129 130 """
Logs the authenticated user out. 130 131 Logs the authenticated user out.
""" 131 132 """
logout(request) 132 133 logout(request)
return Response(status=HTTP_204_NO_CONTENT) 133 134 return Response(status=HTTP_204_NO_CONTENT)
134 135
135 136
class PasswordReset(APIView): 136 137 class PasswordReset(APIView):
""" 137 138 """
Allows user to reset their password. 138 139 Allows user to reset their password.
""" 139 140 """
140 141
def post(self, request, format=None): 141 142 def post(self, request, format=None):
""" 142 143 """
Send a password reset token/link to the provided email. 143 144 Send a password reset token/link to the provided email.
--- 144 145 ---
request_serializer: PasswordResetRequestSerializer 145 146 request_serializer: PasswordResetRequestSerializer
""" 146 147 """
data = PasswordResetRequestSerializer(data=request.data) 147 148 data = PasswordResetRequestSerializer(data=request.data)
data.is_valid(raise_exception=True) 148 149 data.is_valid(raise_exception=True)
user = User.objects.get(email=data['email'].value) 149 150 user = User.objects.get(email=data['email'].value)
token = default_token_generator.make_token(user) 150 151 token = default_token_generator.make_token(user)
151 152
body = ''' 152 153 body = '''
Visit the following link to reset your password: 153 154 Visit the following link to reset your password:
https://flashy.cards/app/reset_password/%d/%s 154 155 https://flashy.cards/app/reset_password/%d/%s
155 156
If you did not request a password reset, no action is required. 156 157 If you did not request a password reset, no action is required.
''' 157 158 '''
flashy/settings.py View file @ be7810a
# Build paths inside the project like this: os.path.join(BASE_DIR, ...) 1 1 # Build paths inside the project like this: os.path.join(BASE_DIR, ...)
import os 2 2 import os
3 3
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) 4 4 BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
5 5
IN_PRODUCTION = 'FLASHY_PRODUCTION' in os.environ 6 6 IN_PRODUCTION = 'FLASHY_PRODUCTION' in os.environ
7 7
DEBUG = not IN_PRODUCTION 8 8 DEBUG = not IN_PRODUCTION
9 9
ALLOWED_HOSTS = ['127.0.0.1', 'flashy.cards'] 10 10 ALLOWED_HOSTS = ['127.0.0.1', 'flashy.cards']
11 11
AUTH_USER_MODEL = 'flashcards.User' 12 12 AUTH_USER_MODEL = 'flashcards.User'
13 13 REST_FRAMEWORK = {
14 'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.LimitOffsetPagination'
15 }
INSTALLED_APPS = ( 14 16 INSTALLED_APPS = (
'simple_email_confirmation', 15 17 'simple_email_confirmation',
'flashcards', 16 18 'flashcards',
'django.contrib.admin', 17 19 'django.contrib.admin',
'django.contrib.admindocs', 18 20 'django.contrib.admindocs',
'django.contrib.auth', 19 21 'django.contrib.auth',
'django.contrib.contenttypes', 20 22 'django.contrib.contenttypes',
'django.contrib.sessions', 21 23 'django.contrib.sessions',
'django.contrib.messages', 22 24 'django.contrib.messages',
'django.contrib.staticfiles', 23 25 'django.contrib.staticfiles',
'django_ses', 24 26 'django_ses',
'rest_framework_swagger', 25 27 'rest_framework_swagger',
'rest_framework', 26 28 'rest_framework',
) 27 29 )
28 30
REST_FRAMEWORK = { 29 31 REST_FRAMEWORK = {
'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.CursorPagination', 30 32 'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.CursorPagination',
'PAGE_SIZE': 20 31 33 'PAGE_SIZE': 20
} 32 34 }
33 35
MIDDLEWARE_CLASSES = ( 34 36 MIDDLEWARE_CLASSES = (
'django.contrib.sessions.middleware.SessionMiddleware', 35 37 'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.common.CommonMiddleware', 36 38 'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware', 37 39 'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware', 38 40 'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.auth.middleware.SessionAuthenticationMiddleware', 39 41 'django.contrib.auth.middleware.SessionAuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware', 40 42 'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware', 41 43 'django.middleware.clickjacking.XFrameOptionsMiddleware',
'django.middleware.security.SecurityMiddleware', 42 44 'django.middleware.security.SecurityMiddleware',
) 43 45 )
44 46
ROOT_URLCONF = 'flashy.urls' 45 47 ROOT_URLCONF = 'flashy.urls'
46 48
AUTHENTICATION_BACKENDS = ( 47 49 AUTHENTICATION_BACKENDS = (
'django.contrib.auth.backends.ModelBackend', 48 50 'django.contrib.auth.backends.ModelBackend',
) 49 51 )
50 52
TEMPLATES = [ 51 53 TEMPLATES = [
{ 52 54 {
'BACKEND': 'django.template.backends.django.DjangoTemplates', 53 55 'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': ['templates/'], 54 56 'DIRS': ['templates/'],
'APP_DIRS': True, 55 57 'APP_DIRS': True,
'OPTIONS': { 56 58 'OPTIONS': {
'context_processors': [ 57 59 'context_processors': [
'django.template.context_processors.debug', 58 60 'django.template.context_processors.debug',
'django.template.context_processors.request', 59 61 'django.template.context_processors.request',
'django.contrib.auth.context_processors.auth', 60 62 'django.contrib.auth.context_processors.auth',
'django.contrib.messages.context_processors.messages', 61 63 'django.contrib.messages.context_processors.messages',
], 62 64 ],
}, 63 65 },
}, 64 66 },
] 65 67 ]
66 68
WSGI_APPLICATION = 'flashy.wsgi.application' 67 69 WSGI_APPLICATION = 'flashy.wsgi.application'
68 70
DATABASES = { 69 71 DATABASES = {
'default': { 70 72 'default': {
'ENGINE': 'django.db.backends.sqlite3', 71 73 'ENGINE': 'django.db.backends.sqlite3',
'NAME': os.path.join(BASE_DIR, 'db.sqlite3'), 72 74 'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
} 73 75 }
} 74 76 }
75 77
if IN_PRODUCTION: 76 78 if IN_PRODUCTION:
DATABASES['default'] = { 77 79 DATABASES['default'] = {
'ENGINE': 'django.db.backends.postgresql_psycopg2', 78 80 'ENGINE': 'django.db.backends.postgresql_psycopg2',
'NAME': 'flashy', 79 81 'NAME': 'flashy',
'USER': 'flashy', 80 82 'USER': 'flashy',
'PASSWORD': os.environ['FLASHY_DB_PW'], 81 83 'PASSWORD': os.environ['FLASHY_DB_PW'],
'HOST': 'localhost', 82 84 'HOST': 'localhost',
'PORT': '', 83 85 'PORT': '',
} 84 86 }
85 87
LANGUAGE_CODE = 'en-us' 86 88 LANGUAGE_CODE = 'en-us'
TIME_ZONE = 'America/Los_Angeles' 87 89 TIME_ZONE = 'America/Los_Angeles'
USE_I18N = True 88 90 USE_I18N = True
USE_L10N = True 89 91 USE_L10N = True
USE_TZ = True 90 92 USE_TZ = True
91 93
STATIC_URL = '/static/' 92 94 STATIC_URL = '/static/'
STATIC_ROOT = 'static' 93 95 STATIC_ROOT = 'static'
94 96
# Four settings just to be sure 95 97 # Four settings just to be sure
EMAIL_FROM = 'noreply@flashy.cards' 96 98 EMAIL_FROM = 'noreply@flashy.cards'
EMAIL_HOST_USER = 'noreply@flashy.cards' 97 99 EMAIL_HOST_USER = 'noreply@flashy.cards'
flashy/urls.py View file @ be7810a
from django.conf.urls import include, url 1 1 from django.conf.urls import include, url
from django.contrib import admin 2 2 from django.contrib import admin
from flashcards.views import SectionViewSet, UserDetail, UserLogin, UserLogout, PasswordReset 3 3 from flashcards.views import SectionViewSet, UserDetail, UserLogin, UserLogout, PasswordReset, UserSectionViewSet
from rest_framework.routers import DefaultRouter 4 4 from rest_framework.routers import DefaultRouter
from flashcards.api import * 5 5 from flashcards.api import *
6 6
router = DefaultRouter() 7 7 router = DefaultRouter()
router.register(r'sections', SectionViewSet) 8 8 router.register(r'sections', SectionViewSet)
9 9
10 router.register(r'users/me/sections', UserSectionViewSet, base_name = 'usersection')
11
urlpatterns = [ 10 12 urlpatterns = [
url(r'^api/docs/', include('rest_framework_swagger.urls')), 11 13 url(r'^api/docs/', include('rest_framework_swagger.urls')),
url(r'^api/users/me$', UserDetail.as_view()), 12 14 url(r'^api/users/me$', UserDetail.as_view()),
url(r'^api/login$', UserLogin.as_view()), 13 15 url(r'^api/login$', UserLogin.as_view()),
url(r'^api/logout$', UserLogout.as_view()), 14 16 url(r'^api/logout$', UserLogout.as_view()),
url(r'^api/reset_password$', PasswordReset.as_view()), 15 17 url(r'^api/reset_password$', PasswordReset.as_view()),
url(r'^api/', include(router.urls)), 16 18 url(r'^api/', include(router.urls)),
url(r'^admin/doc/', include('django.contrib.admindocs.urls')), 17 19 url(r'^admin/doc/', include('django.contrib.admindocs.urls')),
url(r'^admin/', include(admin.site.urls)), 18 20 url(r'^admin/', include(admin.site.urls)),
url(r'^api-auth/', include('rest_framework.urls', namespace='rest_framework')) 19 21 url(r'^api-auth/', include('rest_framework.urls', namespace='rest_framework'
22 ))
] 20 23 ]
21 24