Wednesday 7 August 2019

django 65 migrate to elephantSQL

register at elephantsql.com for a free postgres database on cloud

----------------------------------------
#update database
#django/settings

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.postgresql',
        'NAME': 'pupssizu',
        'USER': 'pupssizu',
        'PASSWORD': 'xxxxx',
        'HOST': 'raja.db.elephantsql.com',
        'PORT': '5432',
    }
}

----------------------------
#pgadmin connect cloud database
pgAdmin 4 create server

lots of database on elephantSQL, locate my database
---------------------------
#django/models

from django.db import models
from django.urls import reverse
from django.contrib.auth.models import User

class Album(models.Model):
    artist = models.CharField(max_length=50)
    album_title = models.CharField(max_length=50)
    genre = models.CharField(max_length=50)
    album_logo = models.TextField(default='iVBOR...')
    date_posted = models.DateTimeField(auto_now_add=True)
    author = models.ForeignKey(User, on_delete=models.CASCADE, default=1)
    price = models.DecimalField(decimal_places=2,max_digits=5, default=10)

    #form submitted without action redirect to detail
    def get_absolute_url(self):
        return reverse('music:detail', kwargs={'pk': self.pk})

    #query album.objects.get(pk=1)
    def __str__(self):
        return self.album_title + ' - ' + self.artist

class Song(models.Model):
    album = models.ForeignKey(Album, on_delete=models.CASCADE)
    file_type = models.CharField(max_length=50)
    song_title = models.CharField(max_length=50)
    is_favorite = models.BooleanField(default=False)

    def __str__(self):
        return self.song_title

        # form submitted without action redirect to detail
    def get_absolute_url(self):
        return reverse('music:detail', kwargs={'pk': self.album.id})

class ShoppingItem(models.Model):
    shopper = models.ForeignKey(User, on_delete=models.CASCADE, default=1)
    album = models.ForeignKey(Album, on_delete=models.CASCADE, blank=True, default=1)
    quantity = models.PositiveIntegerField(blank=True, default=0)

    def __str__(self):
        return 'shopper ' + str(self.shopper) + ' - album ' + str(self.album) + ' - quantity '+str(self.quantity)

class OrderHistory(models.Model):
    order = models.CharField(max_length=50)
    shopper = models.ForeignKey(User, on_delete=models.CASCADE)
    total = models.DecimalField(decimal_places=2,max_digits=7, default=0)
    date = models.DateTimeField(auto_now_add=True)

    def __str__(self):
        return str(self.order)+ ' ' + str(self.shopper) + ' bought '  + str(self.total) + ' on ' + str(self.date)

#powershell python manage.py migrate
------------------------------------
#django create super user
https://docs.djangoproject.com/en/1.8/intro/tutorial02/

super user chuanshuo created
-----------------------------
#django/admin
login django admin as chuanshuo

from django.contrib import admin
from .models import Album, Song, ShoppingItem, OrderHistory

admin.site.register(Album)
admin.site.register(Song)
admin.site.register(ShoppingItem)
admin.site.register(OrderHistory)

-----------------------------------------
#install django-rest-auth
https://django-rest-auth.readthedocs.io/en/latest/installation.html

# django-rest-auth configuration
#django settings
OLD_PASSWORD_FIELD_ENABLED = True
LOGIN_URL = 'http://localhost:3000/login'

#django urls
path('rest-auth/',include('rest_auth.urls')),
path('', include('django.contrib.auth.urls')),

-----------------------------------
#create registration template

https://chuanshuoge2.blogspot.com/2019/05/django-33-reset-password-by-email.html
https://chuanshuoge2.blogspot.com/2019/07/django-53-react-redux-reset-password-by.html

----------------------------------------------
#email setting
#django settings

EMAIL_BACKEND = 'django.core.mail.backends.smtp.EmailBackend'
EMAIL_HOST = 'smtp.gmail.com'
EMAIL_USE_TLS = True
EMAIL_PORT = 587
EMAIL_HOST_USER = 'zchen2014chuanshuo@gmail.com'
EMAIL_HOST_PASSWORD = 'xxxx'

---------------------------------------
#enable cors
#power shell
pip install django-cors-headers

#django setting

INSTALLED_APPS = (
    ...
    'corsheaders',
)

MIDDLEWARE_CLASSES = (
    ...
    'corsheaders.middleware.CorsMiddleware',
)

CORS_ORIGIN_ALLOW_ALL = True
CORS_ALLOW_CREDENTIALS = True

https://chuanshuoge2.blogspot.com/2019/06/django-40-django-react-redux-ftech-token.html
-------------------------------------
#create rest api
#power shell
pip install djangorestframework
pip install django-filter

#django settings
INSTALLED_APPS = [
    ...
    'rest_framework',
]

#django urls
path('api/', include('music.api.urls')),

------------------------------------------
#django rest framework token authentication
#django settings

INSTALLED_APPS = [
...
    'rest_framework',
    'rest_framework.authtoken',
    'django_filters',
]

REST_FRAMEWORK = {
    'DEFAULT_AUTHENTICATION_CLASSES': (
                'rest_framework.authentication.TokenAuthentication',
                ),
    'DEFAULT_PERMISSION_CLASSES':(
        'rest_framework.permissions.IsAuthenticated',
    ),
    'DEFAULT_FILTER_BACKENDS': (
        'django_filters.rest_framework.DjangoFilterBackend',
    ),
}

#powershell
python manage.py migrate

https://chuanshuoge2.blogspot.com/2019/05/django-38-rest-framework-authentication.html

--------------------------
#django/api/urls

from django.urls import path
from rest_framework.urlpatterns import format_suffix_patterns
from music.api import apiview
from rest_framework.authtoken import views

app_name = 'musicAPI'

urlpatterns = [
    path('album_list/', apiview.AlbumList.as_view(), name='AlbumList'),
    path('shoppingItems/', apiview.ShoppingItemsList.as_view(), name='ShoppingItems'),
    path('user_list/', apiview.UserList.as_view(), name='UserList'),
    path('user_register/', apiview.UserRegister.as_view(), name='UserRegister'),
    path('song_list/', apiview.SongList.as_view(), name='SongList'),
    path('cart_checkout/', apiview.CartCheckout.as_view(), name='CartCheckout'),
    path('order_history/', apiview.OrderHistoryList.as_view(), name='OrderHistory'),
    path('album_detail/<int:pk>/', apiview.AlbumDetail.as_view(), name='AlbumDetail'),
    path('shoppingItem/<int:pk>/', apiview.ShoppingItemDetail.as_view(), name='ShoppingItemDetail'),
    path('orderDetail/<int:pk>/', apiview.OrderHistoryDetail.as_view(), name='OrderDetail'),
    path('api-token-auth/', views.obtain_auth_token, name='AuthToken'),
    path('update_password/', apiview.UpdatePassword.as_view(), name='UpdatePassword'),
]

urlpatterns = format_suffix_patterns(urlpatterns)

-------------------------------------------
#django/api/serializers

from rest_framework import serializers
from music.models import Album, Song, ShoppingItem, OrderHistory
from django.contrib.auth.models import User
from django.contrib.auth.password_validation import validate_password

class MusicSerializer(serializers.ModelSerializer):
    class Meta:
        model = Album
        fields = '__all__'

    def __init__(self, *args, **kwargs):
        super(MusicSerializer, self).__init__(*args, **kwargs)
        self.fields['album_logo'] = serializers.CharField(required=True)

class UserSerializer(serializers.ModelSerializer):
    class Meta:
        model = User
        fields = ('id', 'username', 'email')

class SongSerializer(serializers.ModelSerializer):
    class Meta:
        model = Song
        fields = ('id', 'album', 'file_type', 'song_title', 'is_favorite')

class PasswordSerializer(serializers.Serializer):
    old_password = serializers.CharField(required=True)
    new_password = serializers.CharField(required=True)

    def validate_new_password(self, value):
        validate_password(value)
        return value

class ShoppingItemsSerializer(serializers.ModelSerializer):
    class Meta:
        model = ShoppingItem
        fields = ('id', 'shopper', 'album', 'quantity')

class OrderHistorySerializer(serializers.ModelSerializer):
    class Meta:
        model = OrderHistory
        fields = ('id', 'order', 'shopper', 'total', 'date')

-----------------------------------------
#django/api/apiview

from music.models import Album, Song, ShoppingItem, OrderHistory
from music.api.serializers import MusicSerializer, UserSerializer, SongSerializer, PasswordSerializer, ShoppingItemsSerializer, OrderHistorySerializer
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework import status
from django.http import Http404
from django.contrib.auth.models import User
from django.shortcuts import get_object_or_404
from rest_framework.decorators import authentication_classes, permission_classes
from django.contrib.auth.password_validation import validate_password
from django.core import exceptions
from django.forms.models import model_to_dict
from django.conf import settings
import stripe

class AlbumList(APIView):
    def get(self, request, format=None):
        username = request.GET.get('author')
        data_length = request.GET.get('data_length')

        #filter by author
        if username==None:
            albums = Album.objects.all()
        else:
            author_id = get_object_or_404(User, username=username).pk
            albums = Album.objects.filter(author=author_id).order_by('-date_posted')

        #filter by data length
        if data_length!=None:
            try:
                int(data_length)
            except ValueError:
                return Response('data length is invvalid', status=status.HTTP_406_NOT_ACCEPTABLE)
            else:
                albums = albums[:int(data_length)]

        serializer = MusicSerializer(albums, many=True)
        return Response(serializer.data)

    def post(self, request, format=None):
        serializer = MusicSerializer(data=request.data)
        if serializer.is_valid():
            serializer.save()
            return Response(serializer.data, status=status.HTTP_201_CREATED)
        return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)

class AlbumDetail(APIView):

    def get_object(self, pk):
        try:
            return Album.objects.get(pk=pk)
        except Album.DoesNotExist:
            raise Http404

    def get(self, request, pk, format=None):
        album = self.get_object(pk)
        serializer = MusicSerializer(album)
        return Response(serializer.data)

    def put(self, request, pk, format=None):
        album = self.get_object(pk)

        #only owner can edit
        if album.author != request.user:
            return Response({"detail": "You do not have permission to perform this action."},
                            status= status.HTTP_403_FORBIDDEN)

        serializer = MusicSerializer(album, data=request.data)
        if serializer.is_valid():
            serializer.save()
            return Response(serializer.data)
        return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)

    def delete(self, request, pk, format=None):
        album = self.get_object(pk)

        # only owner can delete
        if album.author != request.user:
            return Response({"detail": "You do not have permission to perform this action."},
                            status=status.HTTP_403_FORBIDDEN)

        album.delete()
        return Response(status=status.HTTP_204_NO_CONTENT)

class UserList(APIView):
    def get(self, request, format=None):
        users = User.objects.all()
        serializer = UserSerializer(users, many=True)
        return Response(serializer.data)

@authentication_classes([])
@permission_classes([])
class UserRegister(APIView):
    def post(self, request, format=None):
        serializer = UserSerializer(data=request.data)
        if serializer.is_valid():

            #email has to be unique
            email = request.POST.get('email')
            unique_email = User.objects.filter(email=email).count()
            if(unique_email > 0):
                return Response({'email': 'email already exist'}, status=status.HTTP_406_NOT_ACCEPTABLE)

            #validate password
            password = request.POST.get('password')
            try:
                validate_password(password)
            except exceptions.ValidationError as e:
                return Response(e.messages, status=status.HTTP_406_NOT_ACCEPTABLE)
            else:
                pass
            #register new user
            serializer.save()
            #set password for new user
            username = serializer.data.get('username')
            newUser = User.objects.get(username=username)
            newUser.set_password(password)
            #save password for new user
            newUser.save()

            return Response(serializer.data, status=status.HTTP_201_CREATED)
        return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)

class SongList(APIView):
    def get(self, request, format=None):
        songs = Song.objects.all()
        serializer = SongSerializer(songs, many=True)
        return Response(serializer.data)

class UpdatePassword(APIView):
    def get_object(self, queryset=None):
        return self.request.user

    def put(self, request, *args, **kwargs):
        currentUser = self.get_object()
        serializer = PasswordSerializer(data=request.data)

        if serializer.is_valid():
            # Check old password
            old_password = serializer.data.get("old_password")
            if not currentUser.check_password(old_password):
                return Response({"old_password": ["Wrong password."]},
                                status=status.HTTP_400_BAD_REQUEST)
            # set_password also hashes the password that the user will get
            currentUser.set_password(serializer.data.get("new_password"))
            currentUser.save()
            return Response(status=status.HTTP_204_NO_CONTENT)

        return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)

class ShoppingItemsList(APIView):
    def get(self, request, format=None):
        #for privacy, only return shoppers items
        shoppingItems = ShoppingItem.objects.filter(shopper=request.user)
        serializer = ShoppingItemsSerializer(shoppingItems, many=True)
        return Response(serializer.data)

    def post(self, request, format=None):
        #customer has item in cart
        try:
            revisitedItem = ShoppingItem.objects.get(shopper=request.user,  album=request.data.get('album'))
            revisitedItem.quantity= int(request.data.get('quantity')) + revisitedItem.quantity

            serializer = ShoppingItemsSerializer(data=model_to_dict( revisitedItem ))
            if serializer.is_valid():
                # update database
                revisitedItem.save()
                return Response(serializer.data, status=status.HTTP_202_ACCEPTED)
            return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
        #customer select new item
        except exceptions.ObjectDoesNotExist:
            serializer = ShoppingItemsSerializer(data=request.data)
            if serializer.is_valid():
                serializer.save()
                return Response(serializer.data, status=status.HTTP_201_CREATED)
            return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)

class ShoppingItemDetail(APIView):

    def get_object(self, pk):
        try:
            return ShoppingItem.objects.get(pk=pk)
        except ShoppingItem.DoesNotExist:
            raise Http404

    def get(self, request, pk, format=None):
        shoppingItem = self.get_object(pk)
        serializer = ShoppingItemsSerializer(shoppingItem)
        return Response(serializer.data)

    def put(self, request, pk, format=None):
        shoppintItem = self.get_object(pk)

        #only owner can edit
        if shoppintItem.shopper != request.user:
            return Response({"detail": "You do not have permission to perform this action."},
                            status= status.HTTP_403_FORBIDDEN)

        serializer = ShoppingItemsSerializer(shoppintItem, data=request.data)
        if serializer.is_valid():
            serializer.save()
            return Response(serializer.data)
        return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)

    def delete(self, request, pk, format=None):
        shoppingItem = self.get_object(pk)

        # only owner can delete
        if shoppingItem.shopper != request.user:
            return Response({"detail": "You do not have permission to perform this action."},
                            status=status.HTTP_403_FORBIDDEN)

        shoppingItem.delete()
        return Response(status=status.HTTP_204_NO_CONTENT)

class CartCheckout(APIView):
    def post(self, request, format=None):
        stripe.api_key = settings.STRIPE_SECRET_KEY
        token = request.data.get('token')
        cart = request.data.get('cart')

        try:
            customer=stripe.Customer.create(email=token['email'],source=token['id'])
            charge=stripe.Charge.create(
                amount=int(cart['price']*100),
                currency='cad',
                receipt_email= token['email'],
                customer=customer.id,
                metadata=cart['items'],
            )
            return Response(charge, status=status.HTTP_202_ACCEPTED)
        except Exception as e:
            return Response(e, status=status.HTTP_400_BAD_REQUEST)

class OrderHistoryList(APIView):
    def get(self, request, format=None):
        orders = OrderHistory.objects.filter(shopper=request.user)
        serializer = OrderHistorySerializer(orders, many=True)
        return Response(serializer.data)

    def post(self, request, format=None):
        serializer = OrderHistorySerializer(data=request.data)
        if serializer.is_valid():
            serializer.save()
            return Response(serializer.data, status=status.HTTP_201_CREATED)
        return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)

class OrderHistoryDetail(APIView):
    def get_object(self, pk):
        try:
            return OrderHistory.objects.get(pk=pk)
        except OrderHistory.DoesNotExist:
            raise Http404

    def get(self, request, pk, format=None):
        stripe.api_key = settings.STRIPE_SECRET_KEY
        order = self.get_object(pk)
        try:
            #order is a model boject, request is a python dictionary
            charge=stripe.Charge.retrieve(order.order)
            return Response(charge, status=status.HTTP_200_OK)
        except Exception as e:
            return Response(e, status=status.HTTP_400_BAD_REQUEST)

-------------------------------------------------------
#stripe
#power shell
pip install stripe

#django settings
STRIPE_SECRET_KEY = 'sk_test_xxxxxx'

https://chuanshuoge2.blogspot.com/2019/07/django-62-stripe-checkout.html

No comments:

Post a Comment