Commit 18095ed465f262ac37f8cbef2425ec50ffd48bec
1 parent
2dc11d15d8
Exists in
master
Integrated django-simple-email-confirmation
Showing 14 changed files with 117 additions and 367 deletions Side-by-side Diff
- .gitignore
- flashcards/api.py
- flashcards/migrations/0001_initial.py
- flashcards/migrations/0002_auto_20150429_0248.py
- flashcards/migrations/0003_auto_20150429_0344.py
- flashcards/migrations/0004_auto_20150429_0827.py
- flashcards/migrations/0005_auto_20150430_0557.py
- flashcards/migrations/0006_auto_20150430_0643.py
- flashcards/migrations/0007_auto_20150430_0657.py
- flashcards/models.py
- flashcards/serializers.py
- flashcards/views.py
- flashy/settings.py
- requirements.txt
.gitignore
View file @
18095ed
flashcards/api.py
View file @
18095ed
1 | -from django.http import Http404 | |
1 | +from django.core.mail import send_mail | |
2 | 2 | from rest_framework.views import APIView |
3 | 3 | from rest_framework.response import Response |
4 | 4 | from rest_framework import status |
5 | 5 | from rest_framework.exceptions import ValidationError |
6 | 6 | from flashcards.serializers import * |
7 | -from django.http import HttpResponse | |
8 | -from rest_framework.renderers import JSONRenderer | |
9 | 7 | |
10 | -class JSONResponse(HttpResponse): | |
11 | - """ | |
12 | - An HttpResponse that renders its content into JSON. | |
13 | - """ | |
14 | - def __init__(self, data, **kwargs): | |
15 | - content = JSONRenderer().render(data) | |
16 | - kwargs['content_type'] = 'application/json' | |
17 | - super(JSONResponse, self).__init__(content, **kwargs) | |
18 | 8 | |
19 | - | |
20 | 9 | class UserDetail(APIView): |
21 | - def patch(self, request,format=None): | |
22 | - """ | |
23 | - Updates a user's password after they enter a valid old password. | |
24 | - TODO: email verification | |
25 | - """ | |
26 | - currentUser = request.user | |
27 | - if 'old_password' not in request.data: | |
28 | - raise ValidationError('Old password is required') | |
29 | - if 'new_password' not in request.data: | |
30 | - raise ValidationError('New password is required') | |
31 | - if not request.data['new_password']: | |
32 | - raise ValidationError('Password cannot be blank') | |
33 | - if not currentUser.check_password(request.data['old_password']): | |
34 | - raise ValidationError('Invalid old password') | |
35 | - currentUser.set_password(request.data['new_password']) | |
36 | - currentUser.save() | |
10 | + def patch(self, request, format=None): | |
11 | + """ | |
12 | + Updates a user's password after they enter a valid old password. | |
13 | + TODO: email verification | |
14 | + """ | |
15 | + | |
16 | + if 'old_password' not in request.data: | |
17 | + raise ValidationError('Old password is required') | |
18 | + if 'new_password' not in request.data: | |
19 | + raise ValidationError('New password is required') | |
20 | + if not request.data['new_password']: | |
21 | + raise ValidationError('Password cannot be blank') | |
22 | + | |
23 | + currentuser = request.user | |
24 | + | |
25 | + if not currentuser.check_password(request.data['old_password']): | |
26 | + raise ValidationError('Invalid old password') | |
27 | + | |
28 | + currentuser.set_password(request.data['new_password']) | |
29 | + currentuser.save() | |
30 | + | |
37 | 31 | return Response(status=status.HTTP_204_NO_CONTENT) |
38 | 32 | |
39 | - def get(self, request,format=None): | |
33 | + def get(self, request, format=None): | |
40 | 34 | serializer = UserSerializer(request.user) |
41 | 35 | return Response(serializer.data) |
36 | + | |
37 | + def post(self, request, format=None): | |
38 | + if 'email' not in request.data: | |
39 | + raise ValidationError('Email is required') | |
40 | + if 'password' not in request.data: | |
41 | + raise ValidationError('Password is required') | |
42 | + | |
43 | + email = request.data['email'] | |
44 | + user = User.objects.create_user(email) | |
45 | + | |
46 | + body = ''' | |
47 | + Visit the following link to confirm your email address: | |
48 | + http://flashy.cards/app/verify_email/%s | |
49 | + | |
50 | + If you did not register for Flashy, no action is required. | |
51 | + ''' | |
52 | + | |
53 | + send_mail("Please verify your Flashy account", | |
54 | + body % user.confirmation_key, | |
55 | + "noreply@flashy.cards", | |
56 | + [user.email]) | |
57 | + | |
58 | + return Response(User) |
flashcards/migrations/0001_initial.py
View file @
18095ed
1 | -# -*- coding: utf-8 -*- | |
2 | -from __future__ import unicode_literals | |
3 | - | |
4 | -from django.db import models, migrations | |
5 | -from django.conf import settings | |
6 | - | |
7 | - | |
8 | -class Migration(migrations.Migration): | |
9 | - | |
10 | - dependencies = [ | |
11 | - migrations.swappable_dependency(settings.AUTH_USER_MODEL), | |
12 | - ] | |
13 | - | |
14 | - operations = [ | |
15 | - migrations.CreateModel( | |
16 | - name='Class', | |
17 | - fields=[ | |
18 | - ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)), | |
19 | - ('department', models.CharField(max_length=50)), | |
20 | - ('course_num', models.IntegerField()), | |
21 | - ('name', models.CharField(max_length=50)), | |
22 | - ('professor', models.CharField(max_length=50)), | |
23 | - ('quarter', models.CharField(max_length=4)), | |
24 | - ('members', models.ManyToManyField(to=settings.AUTH_USER_MODEL)), | |
25 | - ], | |
26 | - ), | |
27 | - migrations.CreateModel( | |
28 | - name='Flashcard', | |
29 | - fields=[ | |
30 | - ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)), | |
31 | - ('text', models.CharField(max_length=255)), | |
32 | - ('pushed', models.DateTimeField()), | |
33 | - ('material_date', models.DateTimeField()), | |
34 | - ('hidden', models.CharField(max_length=255, null=True, blank=True)), | |
35 | - ('associated_class', models.ForeignKey(to='flashcards.Class')), | |
36 | - ('author', models.ForeignKey(to=settings.AUTH_USER_MODEL)), | |
37 | - ], | |
38 | - ), | |
39 | - migrations.CreateModel( | |
40 | - name='FlashcardMask', | |
41 | - fields=[ | |
42 | - ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)), | |
43 | - ('ranges', models.CharField(max_length=255)), | |
44 | - ], | |
45 | - ), | |
46 | - migrations.CreateModel( | |
47 | - name='UserFlashcard', | |
48 | - fields=[ | |
49 | - ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)), | |
50 | - ('pulled', models.DateTimeField(null=True)), | |
51 | - ('unpulled', models.DateTimeField(null=True)), | |
52 | - ('flashcard', models.ForeignKey(to='flashcards.Flashcard')), | |
53 | - ('mask', models.ForeignKey(to='flashcards.FlashcardMask')), | |
54 | - ('user', models.ForeignKey(to=settings.AUTH_USER_MODEL)), | |
55 | - ], | |
56 | - ), | |
57 | - migrations.CreateModel( | |
58 | - name='UserFlashCardReview', | |
59 | - fields=[ | |
60 | - ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)), | |
61 | - ('when', models.DateTimeField()), | |
62 | - ('blanked_word', models.CharField(max_length=8)), | |
63 | - ('response', models.CharField(max_length=255, null=True, blank=True)), | |
64 | - ('correct', models.NullBooleanField()), | |
65 | - ('user_flashcard', models.ForeignKey(to='flashcards.UserFlashcard')), | |
66 | - ], | |
67 | - ), | |
68 | - migrations.AddField( | |
69 | - model_name='flashcard', | |
70 | - name='mask', | |
71 | - field=models.ForeignKey(to='flashcards.FlashcardMask', null=True), | |
72 | - ), | |
73 | - migrations.AddField( | |
74 | - model_name='flashcard', | |
75 | - name='previous', | |
76 | - field=models.ForeignKey(to='flashcards.Flashcard', null=True), | |
77 | - ), | |
78 | - ] |
flashcards/migrations/0002_auto_20150429_0248.py
View file @
18095ed
1 | -# -*- coding: utf-8 -*- | |
2 | -from __future__ import unicode_literals | |
3 | - | |
4 | -from django.db import models, migrations | |
5 | - | |
6 | - | |
7 | -class Migration(migrations.Migration): | |
8 | - | |
9 | - dependencies = [ | |
10 | - ('flashcards', '0001_initial'), | |
11 | - ] | |
12 | - | |
13 | - operations = [ | |
14 | - migrations.AlterModelOptions( | |
15 | - name='userflashcard', | |
16 | - options={'ordering': ['-pulled']}, | |
17 | - ), | |
18 | - migrations.AlterUniqueTogether( | |
19 | - name='userflashcard', | |
20 | - unique_together=set([('user', 'flashcard')]), | |
21 | - ), | |
22 | - migrations.AlterIndexTogether( | |
23 | - name='userflashcard', | |
24 | - index_together=set([('user', 'flashcard')]), | |
25 | - ), | |
26 | - ] |
flashcards/migrations/0003_auto_20150429_0344.py
View file @
18095ed
1 | -# -*- coding: utf-8 -*- | |
2 | -from __future__ import unicode_literals | |
3 | - | |
4 | -from django.db import models, migrations | |
5 | - | |
6 | - | |
7 | -class Migration(migrations.Migration): | |
8 | - | |
9 | - dependencies = [ | |
10 | - ('flashcards', '0002_auto_20150429_0248'), | |
11 | - ] | |
12 | - | |
13 | - operations = [ | |
14 | - migrations.AlterModelOptions( | |
15 | - name='class', | |
16 | - options={'ordering': ['-quarter']}, | |
17 | - ), | |
18 | - migrations.AlterModelOptions( | |
19 | - name='flashcard', | |
20 | - options={'ordering': ['-pushed']}, | |
21 | - ), | |
22 | - migrations.RenameField( | |
23 | - model_name='class', | |
24 | - old_name='professor', | |
25 | - new_name='instructor', | |
26 | - ), | |
27 | - migrations.RemoveField( | |
28 | - model_name='flashcard', | |
29 | - name='hidden', | |
30 | - ), | |
31 | - migrations.AddField( | |
32 | - model_name='flashcard', | |
33 | - name='hide_reason', | |
34 | - field=models.CharField(help_text=b'Reason for hiding this card', max_length=255, blank=True), | |
35 | - ), | |
36 | - migrations.AddField( | |
37 | - model_name='flashcard', | |
38 | - name='is_hidden', | |
39 | - field=models.BooleanField(default=False), | |
40 | - ), | |
41 | - migrations.AlterField( | |
42 | - model_name='flashcard', | |
43 | - name='associated_class', | |
44 | - field=models.ForeignKey(help_text=b'The class with which the card is associated', to='flashcards.Class'), | |
45 | - ), | |
46 | - migrations.AlterField( | |
47 | - model_name='flashcard', | |
48 | - name='mask', | |
49 | - field=models.ForeignKey(blank=True, to='flashcards.FlashcardMask', help_text=b'The default mask for this card', null=True), | |
50 | - ), | |
51 | - migrations.AlterField( | |
52 | - model_name='flashcard', | |
53 | - name='material_date', | |
54 | - field=models.DateTimeField(help_text=b'The date with which the card is associated'), | |
55 | - ), | |
56 | - migrations.AlterField( | |
57 | - model_name='flashcard', | |
58 | - name='previous', | |
59 | - field=models.ForeignKey(blank=True, to='flashcards.Flashcard', help_text=b'The previous version of this card, if one exists', null=True), | |
60 | - ), | |
61 | - migrations.AlterField( | |
62 | - model_name='flashcard', | |
63 | - name='pushed', | |
64 | - field=models.DateTimeField(help_text=b'When the card was first pushed', auto_now_add=True), | |
65 | - ), | |
66 | - migrations.AlterField( | |
67 | - model_name='flashcard', | |
68 | - name='text', | |
69 | - field=models.CharField(help_text=b'The text on the card', max_length=255), | |
70 | - ), | |
71 | - migrations.AlterField( | |
72 | - model_name='userflashcard', | |
73 | - name='mask', | |
74 | - field=models.ForeignKey(help_text=b"A mask which overrides the card's mask", to='flashcards.FlashcardMask'), | |
75 | - ), | |
76 | - migrations.AlterField( | |
77 | - model_name='userflashcard', | |
78 | - name='pulled', | |
79 | - field=models.DateTimeField(help_text=b'When the user pulled the card', null=True, blank=True), | |
80 | - ), | |
81 | - migrations.AlterField( | |
82 | - model_name='userflashcard', | |
83 | - name='unpulled', | |
84 | - field=models.DateTimeField(help_text=b'When the user unpulled this card', null=True, blank=True), | |
85 | - ), | |
86 | - migrations.AlterField( | |
87 | - model_name='userflashcardreview', | |
88 | - name='blanked_word', | |
89 | - field=models.CharField(help_text=b'The character range which was blanked', max_length=8, blank=True), | |
90 | - ), | |
91 | - migrations.AlterField( | |
92 | - model_name='userflashcardreview', | |
93 | - name='correct', | |
94 | - field=models.NullBooleanField(help_text=b"The user's self-evaluation of their response"), | |
95 | - ), | |
96 | - migrations.AlterField( | |
97 | - model_name='userflashcardreview', | |
98 | - name='response', | |
99 | - field=models.CharField(help_text=b"The user's response", max_length=255, null=True, blank=True), | |
100 | - ), | |
101 | - migrations.AlterUniqueTogether( | |
102 | - name='class', | |
103 | - unique_together=set([('department', 'course_num', 'quarter', 'instructor')]), | |
104 | - ), | |
105 | - ] |
flashcards/migrations/0004_auto_20150429_0827.py
View file @
18095ed
1 | -# -*- coding: utf-8 -*- | |
2 | -from __future__ import unicode_literals | |
3 | - | |
4 | -from django.db import models, migrations | |
5 | - | |
6 | - | |
7 | -class Migration(migrations.Migration): | |
8 | - | |
9 | - dependencies = [ | |
10 | - ('flashcards', '0003_auto_20150429_0344'), | |
11 | - ] | |
12 | - | |
13 | - operations = [ | |
14 | - migrations.CreateModel( | |
15 | - name='LecturePeriod', | |
16 | - fields=[ | |
17 | - ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)), | |
18 | - ('week_day', models.IntegerField(help_text=b'0-indexed day of week, starting at Monday')), | |
19 | - ('start_time', models.TimeField()), | |
20 | - ('end_time', models.TimeField()), | |
21 | - ], | |
22 | - ), | |
23 | - migrations.RenameModel( | |
24 | - old_name='Class', | |
25 | - new_name='Section', | |
26 | - ), | |
27 | - migrations.RemoveField( | |
28 | - model_name='flashcard', | |
29 | - name='associated_class', | |
30 | - ), | |
31 | - migrations.AddField( | |
32 | - model_name='flashcard', | |
33 | - name='section', | |
34 | - field=models.ForeignKey(default=None, to='flashcards.Section', help_text=b'The section with which the card is associated'), | |
35 | - preserve_default=False, | |
36 | - ), | |
37 | - migrations.AddField( | |
38 | - model_name='lectureperiod', | |
39 | - name='section', | |
40 | - field=models.ForeignKey(to='flashcards.Section'), | |
41 | - ), | |
42 | - ] |
flashcards/migrations/0005_auto_20150430_0557.py
View file @
18095ed
1 | -# -*- coding: utf-8 -*- | |
2 | -from __future__ import unicode_literals | |
3 | - | |
4 | -from django.db import models, migrations | |
5 | - | |
6 | - | |
7 | -class Migration(migrations.Migration): | |
8 | - | |
9 | - dependencies = [ | |
10 | - ('flashcards', '0004_auto_20150429_0827'), | |
11 | - ] | |
12 | - | |
13 | - operations = [ | |
14 | - migrations.RenameField( | |
15 | - model_name='section', | |
16 | - old_name='name', | |
17 | - new_name='course_title', | |
18 | - ), | |
19 | - migrations.RenameField( | |
20 | - model_name='section', | |
21 | - old_name='instructor', | |
22 | - new_name='professor', | |
23 | - ), | |
24 | - migrations.AlterUniqueTogether( | |
25 | - name='section', | |
26 | - unique_together=set([('department', 'course_num', 'quarter', 'professor')]), | |
27 | - ), | |
28 | - ] |
flashcards/migrations/0006_auto_20150430_0643.py
View file @
18095ed
1 | -# -*- coding: utf-8 -*- | |
2 | -from __future__ import unicode_literals | |
3 | - | |
4 | -from django.db import models, migrations | |
5 | - | |
6 | - | |
7 | -class Migration(migrations.Migration): | |
8 | - | |
9 | - dependencies = [ | |
10 | - ('flashcards', '0005_auto_20150430_0557'), | |
11 | - ] | |
12 | - | |
13 | - operations = [ | |
14 | - migrations.AlterField( | |
15 | - model_name='section', | |
16 | - name='course_num', | |
17 | - field=models.CharField(max_length=6), | |
18 | - ), | |
19 | - ] |
flashcards/migrations/0007_auto_20150430_0657.py
View file @
18095ed
1 | -# -*- coding: utf-8 -*- | |
2 | -from __future__ import unicode_literals | |
3 | - | |
4 | -from django.db import models, migrations | |
5 | - | |
6 | - | |
7 | -class Migration(migrations.Migration): | |
8 | - | |
9 | - dependencies = [ | |
10 | - ('flashcards', '0006_auto_20150430_0643'), | |
11 | - ] | |
12 | - | |
13 | - operations = [ | |
14 | - migrations.RenameField( | |
15 | - model_name='section', | |
16 | - old_name='professor', | |
17 | - new_name='instructor', | |
18 | - ), | |
19 | - migrations.AlterUniqueTogether( | |
20 | - name='section', | |
21 | - unique_together=set([('department', 'course_num', 'quarter', 'instructor')]), | |
22 | - ), | |
23 | - ] |
flashcards/models.py
View file @
18095ed
1 | -from django.contrib.auth.models import User | |
1 | +from django.contrib.auth.models import AbstractBaseUser, BaseUserManager | |
2 | +from django.contrib.auth.tests.custom_user import CustomUser | |
2 | 3 | from django.db.models import * |
4 | +from simple_email_confirmation import SimpleEmailConfirmationUserMixin | |
3 | 5 | |
4 | 6 | |
7 | +class UserManager(BaseUserManager): | |
8 | + def create_user(self, email, password=None): | |
9 | + """ | |
10 | + Creates and saves a User with the given email, date of | |
11 | + birth and password. | |
12 | + """ | |
13 | + if not email: | |
14 | + raise ValueError('Users must have an email address') | |
15 | + | |
16 | + user = self.model(email=self.normalize_email(email)) | |
17 | + | |
18 | + user.set_password(password) | |
19 | + user.save(using=self._db) | |
20 | + return user | |
21 | + | |
22 | + def create_superuser(self, email, password): | |
23 | + """ | |
24 | + Creates and saves a superuser with the given email and password. | |
25 | + """ | |
26 | + user = self.create_user(email, password=password) | |
27 | + user.is_admin = True | |
28 | + user.save(using=self._db) | |
29 | + return user | |
30 | + | |
31 | + | |
32 | +class User(AbstractBaseUser, SimpleEmailConfirmationUserMixin): | |
33 | + USERNAME_FIELD = 'email' | |
34 | + REQUIRED_FIELDS = [] | |
35 | + | |
36 | + objects = UserManager() | |
37 | + | |
38 | + email = EmailField( | |
39 | + verbose_name='email address', | |
40 | + max_length=255, | |
41 | + unique=True, | |
42 | + ) | |
43 | + date_joined = DateTimeField(auto_now_add=True) | |
44 | + sections = ManyToManyField('Section') | |
45 | + | |
46 | + | |
5 | 47 | class UserFlashcard(Model): |
6 | 48 | """ |
7 | 49 | Represents the relationship between a user and a flashcard by: |
... | ... | @@ -104,6 +146,7 @@ |
104 | 146 | if self.response: return "answered" |
105 | 147 | return "viewed" |
106 | 148 | |
149 | + | |
107 | 150 | class Section(Model): |
108 | 151 | """ |
109 | 152 | A UCSD course taught by an instructor during a quarter. |
... | ... | @@ -116,7 +159,6 @@ |
116 | 159 | course_title = CharField(max_length=50) |
117 | 160 | instructor = CharField(max_length=50) |
118 | 161 | quarter = CharField(max_length=4) |
119 | - members = ManyToManyField(User) | |
120 | 162 | |
121 | 163 | class Meta: |
122 | 164 | unique_together = (('department', 'course_num', 'quarter', 'instructor'),) |
flashcards/serializers.py
View file @
18095ed
1 | -from flashcards.models import Section, LecturePeriod | |
1 | +from flashcards.models import Section, LecturePeriod, User | |
2 | +from rest_framework.fields import EmailField | |
2 | 3 | from rest_framework.relations import HyperlinkedRelatedField |
3 | 4 | from rest_framework.serializers import HyperlinkedModelSerializer |
4 | -from django.contrib.auth.models import User | |
5 | 5 | |
6 | + | |
6 | 7 | class SectionSerializer(HyperlinkedModelSerializer): |
7 | 8 | lectureperiod_set = HyperlinkedRelatedField(many=True, view_name='lectureperiod-detail', read_only=True) |
9 | + | |
8 | 10 | class Meta: |
9 | 11 | model = Section |
10 | 12 | exclude = ('members',) |
11 | 13 | |
12 | 14 | |
... | ... | @@ -14,10 +16,13 @@ |
14 | 16 | class Meta: |
15 | 17 | model = LecturePeriod |
16 | 18 | |
19 | + | |
17 | 20 | class UserSerializer(HyperlinkedModelSerializer): |
18 | 21 | """ |
19 | 22 | """ |
23 | + email = EmailField(required=False) | |
24 | + | |
20 | 25 | class Meta: |
21 | - model = User | |
22 | - fields = ("email", "is_active", "last_login", "date_joined") | |
26 | + model = User | |
27 | + fields = ("email", "is_active", "last_login", "date_joined") |
flashcards/views.py
View file @
18095ed
... | ... | @@ -3,18 +3,20 @@ |
3 | 3 | from rest_framework.permissions import IsAuthenticatedOrReadOnly |
4 | 4 | from rest_framework.viewsets import ModelViewSet |
5 | 5 | from rest_framework.pagination import PageNumberPagination |
6 | -from django.core.paginator import Paginator | |
7 | 6 | |
7 | + | |
8 | 8 | class StandardResultsSetPagination(PageNumberPagination): |
9 | 9 | page_size = 40 |
10 | 10 | page_size_query_param = 'page_size' |
11 | 11 | max_page_size = 1000 |
12 | 12 | |
13 | + | |
13 | 14 | class SectionViewSet(ModelViewSet): |
14 | 15 | queryset = Section.objects.all() |
15 | 16 | serializer_class = SectionSerializer |
16 | 17 | permission_classes = (IsAuthenticatedOrReadOnly,) |
17 | 18 | pagination_class = StandardResultsSetPagination |
19 | + | |
18 | 20 | |
19 | 21 | class LecturePeriodViewSet(ModelViewSet): |
20 | 22 | queryset = LecturePeriod.objects.all() |
flashy/settings.py
View file @
18095ed
... | ... | @@ -15,22 +15,17 @@ |
15 | 15 | |
16 | 16 | BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) |
17 | 17 | |
18 | - | |
19 | -# Quick-start development settings - unsuitable for production | |
20 | -# See https://docs.djangoproject.com/en/1.8/howto/deployment/checklist/ | |
21 | - | |
22 | -# SECURITY WARNING: keep the secret key used in production secret! | |
23 | -SECRET_KEY = '8)#-j&8dghe-y&v4a%r6u#y@r86&6nfv%k$(a((r-zh$m3ct!9' | |
24 | - | |
25 | 18 | # SECURITY WARNING: don't run with debug turned on in production! |
26 | 19 | DEBUG = True |
27 | 20 | |
28 | 21 | ALLOWED_HOSTS = [] |
29 | 22 | |
30 | - | |
23 | +AUTH_USER_MODEL = 'flashcards.User' | |
31 | 24 | # Application definition |
32 | 25 | |
33 | 26 | INSTALLED_APPS = ( |
27 | + 'simple_email_confirmation', | |
28 | + 'flashcards', | |
34 | 29 | 'django.contrib.admin', |
35 | 30 | 'django.contrib.admindocs', |
36 | 31 | 'django.contrib.auth', |
37 | 32 | |
... | ... | @@ -38,8 +33,10 @@ |
38 | 33 | 'django.contrib.sessions', |
39 | 34 | 'django.contrib.messages', |
40 | 35 | 'django.contrib.staticfiles', |
36 | + 'django_ses', | |
41 | 37 | 'rest_framework', |
42 | - 'flashcards', | |
38 | + | |
39 | + | |
43 | 40 | ) |
44 | 41 | |
45 | 42 | REST_FRAMEWORK = { |
... | ... | @@ -109,4 +106,8 @@ |
109 | 106 | |
110 | 107 | STATIC_URL = '/static/' |
111 | 108 | STATIC_ROOT = 'static' |
109 | + | |
110 | +EMAIL_BACKEND = 'django_ses.SESBackend' | |
111 | + | |
112 | +SECRET_KEY = os.environ.get('SECRET_KEY', 'LOL DEFAULT SECRET KEY') |