├── .gitignore ├── Procfile ├── README.md ├── api ├── __init__.py ├── admin.py ├── apps.py ├── category │ ├── __init__.py │ ├── admin.py │ ├── apps.py │ ├── migrations │ │ ├── 0001_initial.py │ │ ├── 0002_auto_20201107_1405.py │ │ └── __init__.py │ ├── models.py │ ├── serializers.py │ ├── tests.py │ ├── urls.py │ └── views.py ├── migrations │ ├── 0001_initial.py │ └── __init__.py ├── models.py ├── order │ ├── __init__.py │ ├── admin.py │ ├── apps.py │ ├── migrations │ │ ├── 0001_initial.py │ │ └── __init__.py │ ├── models.py │ ├── serializers.py │ ├── tests.py │ ├── urls.py │ └── views.py ├── payment │ ├── __init__.py │ ├── admin.py │ ├── apps.py │ ├── migrations │ │ └── __init__.py │ ├── models.py │ ├── tests.py │ ├── urls.py │ └── views.py ├── product │ ├── __init__.py │ ├── admin.py │ ├── apps.py │ ├── migrations │ │ ├── 0001_initial.py │ │ └── __init__.py │ ├── models.py │ ├── serializers.py │ ├── tests.py │ ├── urls.py │ └── views.py ├── tests.py ├── urls.py ├── user │ ├── __init__.py │ ├── admin.py │ ├── apps.py │ ├── migrations │ │ ├── 0001_initial.py │ │ └── __init__.py │ ├── models.py │ ├── serializers.py │ ├── tests.py │ ├── urls.py │ └── views.py └── views.py ├── db.json ├── ecommerce ├── __init__.py ├── asgi.py ├── settings.py ├── urls.py └── wsgi.py ├── manage.py └── requirements.txt /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 8 | 9 | # Distribution / packaging 10 | .Python 11 | build/ 12 | develop-eggs/ 13 | dist/ 14 | downloads/ 15 | eggs/ 16 | .eggs/ 17 | lib/ 18 | lib64/ 19 | parts/ 20 | sdist/ 21 | var/ 22 | wheels/ 23 | share/python-wheels/ 24 | *.egg-info/ 25 | .installed.cfg 26 | *.egg 27 | MANIFEST 28 | 29 | # PyInstaller 30 | # Usually these files are written by a python script from a template 31 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 32 | *.manifest 33 | *.spec 34 | 35 | # Installer logs 36 | pip-log.txt 37 | pip-delete-this-directory.txt 38 | 39 | # Unit test / coverage reports 40 | htmlcov/ 41 | .tox/ 42 | .nox/ 43 | .coverage 44 | .coverage.* 45 | .cache 46 | nosetests.xml 47 | coverage.xml 48 | *.cover 49 | *.py,cover 50 | .hypothesis/ 51 | .pytest_cache/ 52 | cover/ 53 | 54 | # Translations 55 | *.mo 56 | *.pot 57 | 58 | # Django stuff: 59 | *.log 60 | local_settings.py 61 | db.sqlite3 62 | db.sqlite3-journal 63 | 64 | # Flask stuff: 65 | instance/ 66 | .webassets-cache 67 | 68 | # Scrapy stuff: 69 | .scrapy 70 | 71 | # Sphinx documentation 72 | docs/_build/ 73 | 74 | # PyBuilder 75 | .pybuilder/ 76 | target/ 77 | 78 | # Jupyter Notebook 79 | .ipynb_checkpoints 80 | 81 | # IPython 82 | profile_default/ 83 | ipython_config.py 84 | 85 | # pyenv 86 | # For a library or package, you might want to ignore these files since the code is 87 | # intended to run in multiple environments; otherwise, check them in: 88 | # .python-version 89 | 90 | # pipenv 91 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. 92 | # However, in case of collaboration, if having platform-specific dependencies or dependencies 93 | # having no cross-platform support, pipenv may install dependencies that don't work, or not 94 | # install all needed dependencies. 95 | #Pipfile.lock 96 | 97 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow 98 | __pypackages__/ 99 | 100 | # Celery stuff 101 | celerybeat-schedule 102 | celerybeat.pid 103 | 104 | # SageMath parsed files 105 | *.sage.py 106 | 107 | # Environments 108 | .env 109 | .venv 110 | env/ 111 | venv/ 112 | ENV/ 113 | env.bak/ 114 | venv.bak/ 115 | 116 | # Spyder project settings 117 | .spyderproject 118 | .spyproject 119 | 120 | # Rope project settings 121 | .ropeproject 122 | 123 | # mkdocs documentation 124 | /site 125 | 126 | # mypy 127 | .mypy_cache/ 128 | .dmypy.json 129 | dmypy.json 130 | 131 | # Pyre type checker 132 | .pyre/ 133 | 134 | # pytype static type analyzer 135 | .pytype/ 136 | 137 | # Cython debug symbols 138 | cython_debug/ 139 | .vscode 140 | TODO 141 | SPEC.md 142 | SQL.md -------------------------------------------------------------------------------- /Procfile: -------------------------------------------------------------------------------- 1 | web: gunicorn ecommerce.wsgi -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

Ecommerce Rest API


2 | 3 | 4 |

5 | An Ecommerce Store Backend to Sell Products. It accepts Paypal/Stripe Payment. Built with Python/Django Rest Framework. 6 |

7 | 8 | ## Table of Contents 9 | 10 | - [Introduction](#introduction) 11 | - [Features](#features) 12 | - [Installation Process](#installation-process) 13 | - [Project Demo](#project-demo) 14 | - [Feedback](#feedback) 15 | 16 | ## Introduction 17 | 18 | DRF-Ecommerce-API provides API endpoints to Sell Physical Products through Paypal/Stripe Payment. Built with Python/Django and 100% free to use. 19 | 20 | ## Features 21 | 22 | A few of the things you can do with this app: 23 | 24 | * Admin can create/update/delete Product Category 25 | * Admin can create/update/delete Product Detail 26 | * Authenticated Users can make POST requests to Product Category & Product Detail 27 | * Unauthenticated Users can only make GET requests to Product Category & Product Detail 28 | * Users can SignUp to be authorized 29 | * Authorized Users can make Payment & Order Products 30 | 31 | ## Installation Process 32 | 33 | **Installation Process (Windows)** 34 | 35 | 1. Create a Virtual Environment `virtualenv projectenv` 36 | 2. Go To environment Directory `cd projectenv/scripts` 37 | 3. Activate Virtual Environment `activate` 38 | 4. Clone This Project `git clone https://github.com/mahmud-sajib/DRF-Ecommerce-API.git` 39 | 5. Go To Project Directory `cd DRF-Ecommerce-API` 40 | 6. Install Required Package `pip install -r requirements.txt` 41 | 7. Migrate Database `python manage.py migrate` 42 | 8. Finally Run The Project `python manage.py runserver` 43 | 44 | ## Project Demo 45 | 46 | ### Live Demo 47 | 48 | Live Project url: [ecom-rest.herokuapp.com](https://ecom-rest.herokuapp.com/) 49 | 50 | ### API Documentation Page 51 | 52 |

53 | 54 |

55 | 56 | ## Feedback 57 | 58 | Feel free to [file an issue](https://github.com/mahmud-sajib/DRF-Ecommerce-API/issues/new). If you wish to contribute, please feel free to do so! 59 | 60 | -------------------------------------------------------------------------------- /api/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mahmud-sajib/Django-Ecommerce-API/36d42f67a08ab626150b6ed7e7e952b0ee68d581/api/__init__.py -------------------------------------------------------------------------------- /api/admin.py: -------------------------------------------------------------------------------- 1 | from django.contrib import admin 2 | 3 | # Register your models here. 4 | -------------------------------------------------------------------------------- /api/apps.py: -------------------------------------------------------------------------------- 1 | from django.apps import AppConfig 2 | 3 | 4 | class ApiConfig(AppConfig): 5 | name = 'api' 6 | -------------------------------------------------------------------------------- /api/category/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mahmud-sajib/Django-Ecommerce-API/36d42f67a08ab626150b6ed7e7e952b0ee68d581/api/category/__init__.py -------------------------------------------------------------------------------- /api/category/admin.py: -------------------------------------------------------------------------------- 1 | from django.contrib import admin 2 | from .models import Category 3 | 4 | # Register your models here. 5 | 6 | admin.site.register(Category) 7 | -------------------------------------------------------------------------------- /api/category/apps.py: -------------------------------------------------------------------------------- 1 | from django.apps import AppConfig 2 | 3 | 4 | class CategoryConfig(AppConfig): 5 | name = 'category' 6 | -------------------------------------------------------------------------------- /api/category/migrations/0001_initial.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.0.8 on 2020-11-07 05:26 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | initial = True 9 | 10 | dependencies = [ 11 | ] 12 | 13 | operations = [ 14 | migrations.CreateModel( 15 | name='Category', 16 | fields=[ 17 | ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), 18 | ('name', models.CharField(max_length=50)), 19 | ('description', models.CharField(max_length=250)), 20 | ('created_at', models.DateTimeField(auto_now_add=True)), 21 | ('updated_at', models.DateTimeField(auto_now=True)), 22 | ], 23 | ), 24 | ] 25 | -------------------------------------------------------------------------------- /api/category/migrations/0002_auto_20201107_1405.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.0.8 on 2020-11-07 08:05 2 | 3 | from django.db import migrations 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ('category', '0001_initial'), 10 | ] 11 | 12 | operations = [ 13 | migrations.AlterModelOptions( 14 | name='category', 15 | options={'verbose_name_plural': 'Categories'}, 16 | ), 17 | ] 18 | -------------------------------------------------------------------------------- /api/category/migrations/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mahmud-sajib/Django-Ecommerce-API/36d42f67a08ab626150b6ed7e7e952b0ee68d581/api/category/migrations/__init__.py -------------------------------------------------------------------------------- /api/category/models.py: -------------------------------------------------------------------------------- 1 | from django.db import models 2 | 3 | # Create your models here. 4 | 5 | class Category(models.Model): 6 | name = models.CharField(max_length=50) 7 | description = models.CharField(max_length=250) 8 | created_at = models.DateTimeField(auto_now_add=True) 9 | updated_at = models.DateTimeField(auto_now=True) 10 | 11 | def __str__(self): 12 | return f"{self.name}" 13 | 14 | class Meta: 15 | verbose_name_plural="Categories" 16 | 17 | 18 | -------------------------------------------------------------------------------- /api/category/serializers.py: -------------------------------------------------------------------------------- 1 | from rest_framework import serializers 2 | 3 | from .models import Category 4 | 5 | class CategorySerializer(serializers.ModelSerializer): 6 | class Meta: 7 | # Model to be serialized 8 | model = Category 9 | # Fields to be serialized 10 | fields = ('name', 'description') -------------------------------------------------------------------------------- /api/category/tests.py: -------------------------------------------------------------------------------- 1 | from django.test import TestCase 2 | 3 | # Create your tests here. 4 | -------------------------------------------------------------------------------- /api/category/urls.py: -------------------------------------------------------------------------------- 1 | from rest_framework import routers 2 | from django.urls import path, include 3 | from . import views 4 | 5 | router = routers.DefaultRouter() 6 | router.register(r'', views.CategoryViewSet) 7 | 8 | urlpatterns = [ 9 | path('', include(router.urls)), 10 | ] 11 | -------------------------------------------------------------------------------- /api/category/views.py: -------------------------------------------------------------------------------- 1 | from rest_framework import viewsets 2 | from .serializers import CategorySerializer 3 | from .models import Category 4 | 5 | # Create your views here. 6 | class CategoryViewSet(viewsets.ModelViewSet): 7 | # Operations to be performed 8 | queryset = Category.objects.all().order_by('-created_at') 9 | # Class responsible for serializing the data 10 | serializer_class = CategorySerializer -------------------------------------------------------------------------------- /api/migrations/0001_initial.py: -------------------------------------------------------------------------------- 1 | from django.db import migrations 2 | from api.user.models import CustomUser 3 | 4 | class Migration(migrations.Migration): 5 | def seed_data(apps, schema_editor): 6 | user = CustomUser(name="mahmud", email="sajibforest@gmail.com", is_staff=True, is_superuser=True, phone="01553848755", gender="Male") 7 | 8 | user.set_password("123456") 9 | 10 | user.save() 11 | 12 | dependencies = [ 13 | 14 | ] 15 | 16 | operations = [ 17 | migrations.RunPython(seed_data) 18 | ] -------------------------------------------------------------------------------- /api/migrations/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mahmud-sajib/Django-Ecommerce-API/36d42f67a08ab626150b6ed7e7e952b0ee68d581/api/migrations/__init__.py -------------------------------------------------------------------------------- /api/models.py: -------------------------------------------------------------------------------- 1 | from django.db import models 2 | 3 | # Create your models here. 4 | -------------------------------------------------------------------------------- /api/order/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mahmud-sajib/Django-Ecommerce-API/36d42f67a08ab626150b6ed7e7e952b0ee68d581/api/order/__init__.py -------------------------------------------------------------------------------- /api/order/admin.py: -------------------------------------------------------------------------------- 1 | from django.contrib import admin 2 | from .models import Order 3 | 4 | # Register your models here. 5 | admin.site.register(Order) 6 | -------------------------------------------------------------------------------- /api/order/apps.py: -------------------------------------------------------------------------------- 1 | from django.apps import AppConfig 2 | 3 | 4 | class OrderConfig(AppConfig): 5 | name = 'order' 6 | -------------------------------------------------------------------------------- /api/order/migrations/0001_initial.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.0.8 on 2020-11-10 10:54 2 | 3 | from django.conf import settings 4 | from django.db import migrations, models 5 | import django.db.models.deletion 6 | 7 | 8 | class Migration(migrations.Migration): 9 | 10 | initial = True 11 | 12 | dependencies = [ 13 | migrations.swappable_dependency(settings.AUTH_USER_MODEL), 14 | ] 15 | 16 | operations = [ 17 | migrations.CreateModel( 18 | name='Order', 19 | fields=[ 20 | ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), 21 | ('product_names', models.CharField(max_length=150)), 22 | ('total_products', models.CharField(default=0, max_length=250)), 23 | ('total_amount', models.CharField(default=0, max_length=250)), 24 | ('created_at', models.DateTimeField(auto_now_add=True)), 25 | ('updated_at', models.DateTimeField(auto_now=True)), 26 | ('transaction_id', models.CharField(default=0, max_length=150)), 27 | ('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)), 28 | ], 29 | ), 30 | ] 31 | -------------------------------------------------------------------------------- /api/order/migrations/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mahmud-sajib/Django-Ecommerce-API/36d42f67a08ab626150b6ed7e7e952b0ee68d581/api/order/migrations/__init__.py -------------------------------------------------------------------------------- /api/order/models.py: -------------------------------------------------------------------------------- 1 | from django.db import models 2 | from api.user.models import CustomUser 3 | from api.product.models import Product 4 | 5 | # Create your models here. 6 | 7 | class Order(models.Model): 8 | user = models.ForeignKey(CustomUser, on_delete=models.CASCADE) 9 | product_names = models.CharField(max_length=150) 10 | total_products = models.CharField(max_length=250, default=0) 11 | total_amount = models.CharField(max_length=250, default=0) 12 | created_at = models.DateTimeField(auto_now_add=True) 13 | updated_at = models.DateTimeField(auto_now=True) 14 | transaction_id = models.CharField(max_length=150, default=0) 15 | 16 | def __str__(self): 17 | return f"{self.product_name}" 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /api/order/serializers.py: -------------------------------------------------------------------------------- 1 | from rest_framework import serializers 2 | 3 | from .models import Order 4 | 5 | class OrderSerializer(serializers.ModelSerializer): 6 | 7 | class Meta: 8 | # Model to be serialized 9 | model = Order 10 | # Fields to be serialized 11 | fields = ('user',) -------------------------------------------------------------------------------- /api/order/tests.py: -------------------------------------------------------------------------------- 1 | from django.test import TestCase 2 | 3 | # Create your tests here. 4 | -------------------------------------------------------------------------------- /api/order/urls.py: -------------------------------------------------------------------------------- 1 | from rest_framework import routers 2 | from django.urls import path, include 3 | from . import views 4 | 5 | router = routers.DefaultRouter() 6 | router.register(r'', views.OrderViewSet) 7 | 8 | urlpatterns = [ 9 | path('add-order///', views.add_order, name='add-order'), 10 | path('', include(router.urls)), 11 | ] 12 | -------------------------------------------------------------------------------- /api/order/views.py: -------------------------------------------------------------------------------- 1 | from rest_framework import viewsets 2 | from .serializers import OrderSerializer 3 | from .models import Order 4 | from django.http import JsonResponse 5 | from django.contrib.auth import get_user_model 6 | from django.views.decorators.csrf import csrf_exempt 7 | 8 | 9 | # User Authentication view 10 | def validate_user_session(id, token): 11 | """Allow ordering for only authenticated users""" 12 | UserModel = get_user_model() 13 | 14 | try: 15 | user = UserModel.objects.get(pk=id) 16 | 17 | # Check if Session Token matches User Token 18 | if user.session_token == token: 19 | return True # User is authenticated 20 | else: 21 | return False # User is unauthenticated 22 | 23 | except UserModel.DoesNotExist: 24 | return False 25 | 26 | 27 | # Add Order view 28 | def add_order(request, id, token): 29 | if not validate_user_session(id, token): 30 | return JsonResponse({'error':'Please login again', 'code':'1'}) 31 | 32 | if request.method == "POST": 33 | user_id = id 34 | transaction_id = request.POST['transaction_id'] 35 | amount = request.POST['amount'] 36 | products = request.POST['products'] 37 | total_no_products = len(products.split(',')[:-1]) 38 | 39 | UserModel = get_user_model() 40 | 41 | try: 42 | user = UserModel.objects.get(pk=user_id) 43 | 44 | except UserModel.DoesNotExist: 45 | return JsonResponse({'error':'User does not exist'}) 46 | 47 | order_detail = Order(user=user, product_names=products, total_products=total_no_products, total_amount=amount, transaction_id=transaction_id) 48 | 49 | order_detail.save() 50 | 51 | return JsonResponse({'success':True, 'error':False, 'msg':'Order placed successfully'}) 52 | 53 | # Order View 54 | class OrderViewSet(viewsets.ModelViewSet): 55 | # Operations to be performed 56 | queryset = Order.objects.all().order_by('id') 57 | # Class responsible for serializing the data 58 | serializer_class = OrderSerializer 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | -------------------------------------------------------------------------------- /api/payment/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mahmud-sajib/Django-Ecommerce-API/36d42f67a08ab626150b6ed7e7e952b0ee68d581/api/payment/__init__.py -------------------------------------------------------------------------------- /api/payment/admin.py: -------------------------------------------------------------------------------- 1 | from django.contrib import admin 2 | 3 | # Register your models here. 4 | -------------------------------------------------------------------------------- /api/payment/apps.py: -------------------------------------------------------------------------------- 1 | from django.apps import AppConfig 2 | 3 | 4 | class PaymentConfig(AppConfig): 5 | name = 'payment' 6 | -------------------------------------------------------------------------------- /api/payment/migrations/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mahmud-sajib/Django-Ecommerce-API/36d42f67a08ab626150b6ed7e7e952b0ee68d581/api/payment/migrations/__init__.py -------------------------------------------------------------------------------- /api/payment/models.py: -------------------------------------------------------------------------------- 1 | from django.db import models 2 | 3 | # Create your models here. 4 | -------------------------------------------------------------------------------- /api/payment/tests.py: -------------------------------------------------------------------------------- 1 | from django.test import TestCase 2 | 3 | # Create your tests here. 4 | -------------------------------------------------------------------------------- /api/payment/urls.py: -------------------------------------------------------------------------------- 1 | from django.urls import path, include 2 | from . import views 3 | 4 | urlpatterns = [ 5 | path('generate-token///', views.generate_token, name='generate-token'), 6 | path('process-payment///', views.process_payment, name='process-payment'), 7 | ] 8 | -------------------------------------------------------------------------------- /api/payment/views.py: -------------------------------------------------------------------------------- 1 | from django.shortcuts import render 2 | from django.http import HttpResponse, JsonResponse 3 | from django.contrib.auth.decorators import login_required 4 | from django.contrib.auth import get_user_model 5 | from django.views.decorators.csrf import csrf_exempt 6 | 7 | import braintree 8 | 9 | # Create your views here. 10 | 11 | # BrainTree Credentials 12 | gateway = braintree.BraintreeGateway( 13 | braintree.Configuration( 14 | braintree.Environment.Sandbox, 15 | merchant_id="4ymx8dm8hyz2x6rt", 16 | public_key="6wt4d62db8rfsy7b", 17 | private_key="4741def360e584d5141d06e9b0f64dfc" 18 | ) 19 | ) 20 | 21 | # User Authentication view 22 | def validate_user_session(id, token): 23 | """Allow payment completion for only authenticated users""" 24 | UserModel = get_user_model() 25 | 26 | try: 27 | user = UserModel.objects.get(pk=id) 28 | 29 | # Check if Session Token matches User Token 30 | if user.session_token == token: 31 | return True # User is authenticated 32 | else: 33 | return False # User is unauthenticated 34 | 35 | except UserModel.DoesNotExist: 36 | return False 37 | 38 | # Generate Payment Token view 39 | @csrf_exempt 40 | def generate_token(request, id, token): 41 | if not validate_user_session(id, token): 42 | return JsonResponse({'error':'Inavlid Session. Please login again'}) 43 | 44 | # Generate token if user is authenticated 45 | return JsonResponse({'clientToken':gateway.client_token.generate(), 'success':True}) 46 | 47 | # Process Payment view 48 | @csrf_exempt 49 | def process_payment(request, id, token): 50 | if not validate_user_session(id, token): 51 | return JsonResponse({'error':'Inavlid Session. Please login again'}) 52 | 53 | # Get the payment nonce from client 54 | nonce_from_the_client = request.POST["paymentMethodNonce"] 55 | # Get the amount from client 56 | amount_from_the_client = request.POST["amount"] 57 | 58 | # Process the payment 59 | result = gateway.transaction.sale({ 60 | "amount": amount_from_the_client, 61 | "payment_method_nonce": nonce_from_the_client, 62 | "options": { 63 | "submit_for_settlement": True 64 | } 65 | }) 66 | 67 | # Post payment results 68 | if result.is_success: 69 | return JsonResponse({ 70 | 'success':result.is_success, 71 | 'transaction':{ 72 | 'id':result.transaction.id, 73 | 'amount':result.transaction.amount 74 | } 75 | }) 76 | else: 77 | return JsonResponse({'error':True, 'success':False}) 78 | 79 | 80 | -------------------------------------------------------------------------------- /api/product/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mahmud-sajib/Django-Ecommerce-API/36d42f67a08ab626150b6ed7e7e952b0ee68d581/api/product/__init__.py -------------------------------------------------------------------------------- /api/product/admin.py: -------------------------------------------------------------------------------- 1 | from django.contrib import admin 2 | from .models import Product 3 | 4 | # Register your models here. 5 | admin.site.register(Product) 6 | -------------------------------------------------------------------------------- /api/product/apps.py: -------------------------------------------------------------------------------- 1 | from django.apps import AppConfig 2 | 3 | 4 | class ProductConfig(AppConfig): 5 | name = 'product' 6 | -------------------------------------------------------------------------------- /api/product/migrations/0001_initial.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.0.8 on 2020-11-07 08:05 2 | 3 | from django.db import migrations, models 4 | import django.db.models.deletion 5 | 6 | 7 | class Migration(migrations.Migration): 8 | 9 | initial = True 10 | 11 | dependencies = [ 12 | ('category', '0002_auto_20201107_1405'), 13 | ] 14 | 15 | operations = [ 16 | migrations.CreateModel( 17 | name='Product', 18 | fields=[ 19 | ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), 20 | ('name', models.CharField(max_length=50)), 21 | ('description', models.CharField(max_length=250)), 22 | ('created_at', models.DateTimeField(auto_now_add=True)), 23 | ('updated_at', models.DateTimeField(auto_now=True)), 24 | ('price', models.CharField(max_length=50)), 25 | ('stock', models.CharField(max_length=50)), 26 | ('image', models.ImageField(blank=True, null=True, upload_to='products/%Y/%m/%d')), 27 | ('is_active', models.BooleanField(default=True)), 28 | ('category', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='category.Category')), 29 | ], 30 | ), 31 | ] 32 | -------------------------------------------------------------------------------- /api/product/migrations/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mahmud-sajib/Django-Ecommerce-API/36d42f67a08ab626150b6ed7e7e952b0ee68d581/api/product/migrations/__init__.py -------------------------------------------------------------------------------- /api/product/models.py: -------------------------------------------------------------------------------- 1 | from django.db import models 2 | from api.category.models import Category 3 | 4 | # Create your models here. 5 | 6 | class Product(models.Model): 7 | name = models.CharField(max_length=50) 8 | description = models.CharField(max_length=250) 9 | created_at = models.DateTimeField(auto_now_add=True) 10 | updated_at = models.DateTimeField(auto_now=True) 11 | price = models.CharField(max_length=50) 12 | stock = models.CharField(max_length=50) 13 | image = models.ImageField(upload_to='products/%Y/%m/%d', blank=True, null=True) 14 | is_active = models.BooleanField(default=True) 15 | category = models.ForeignKey(Category, on_delete=models.SET_NULL, blank=True, null=True) 16 | 17 | def __str__(self): 18 | return f"{self.name}" 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /api/product/serializers.py: -------------------------------------------------------------------------------- 1 | from rest_framework import serializers 2 | 3 | from .models import Product 4 | 5 | class ProductSerializer(serializers.ModelSerializer): 6 | # Get the image url by serializing `ImageField` 7 | image = serializers.ImageField(max_length=None, allow_empty_file=False, allow_null=True, required=False) 8 | 9 | class Meta: 10 | # Model to be serialized 11 | model = Product 12 | # Fields to be serialized 13 | fields = ('id', 'name', 'description', 'price', 'stock', 'image', 'category') -------------------------------------------------------------------------------- /api/product/tests.py: -------------------------------------------------------------------------------- 1 | from django.test import TestCase 2 | 3 | # Create your tests here. 4 | -------------------------------------------------------------------------------- /api/product/urls.py: -------------------------------------------------------------------------------- 1 | from rest_framework import routers 2 | from django.urls import path, include 3 | from . import views 4 | 5 | router = routers.DefaultRouter() 6 | router.register(r'', views.ProductViewSet) 7 | 8 | urlpatterns = [ 9 | path('', include(router.urls)), 10 | ] 11 | -------------------------------------------------------------------------------- /api/product/views.py: -------------------------------------------------------------------------------- 1 | from rest_framework import viewsets 2 | from .serializers import ProductSerializer 3 | from .models import Product 4 | 5 | # Create your views here. 6 | 7 | # Product View 8 | class ProductViewSet(viewsets.ModelViewSet): 9 | # Operations to be performed 10 | queryset = Product.objects.all().order_by('id') 11 | # Class responsible for serializing the data 12 | serializer_class = ProductSerializer 13 | -------------------------------------------------------------------------------- /api/tests.py: -------------------------------------------------------------------------------- 1 | from django.test import TestCase 2 | 3 | # Create your tests here. 4 | -------------------------------------------------------------------------------- /api/urls.py: -------------------------------------------------------------------------------- 1 | from django.urls import path, include 2 | 3 | 4 | 5 | # from rest_framework.authtoken import views 6 | 7 | from api import views 8 | 9 | urlpatterns = [ 10 | path('', views.home, name="home"), 11 | path('category/', include('api.category.urls')), 12 | path('product/', include('api.product.urls')), 13 | path('user/', include('api.user.urls')), 14 | path('order/', include('api.order.urls')), 15 | path('payment/', include('api.payment.urls')), 16 | ] -------------------------------------------------------------------------------- /api/user/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mahmud-sajib/Django-Ecommerce-API/36d42f67a08ab626150b6ed7e7e952b0ee68d581/api/user/__init__.py -------------------------------------------------------------------------------- /api/user/admin.py: -------------------------------------------------------------------------------- 1 | from django.contrib import admin 2 | from .models import CustomUser 3 | 4 | # Register your models here. 5 | admin.site.register(CustomUser) 6 | -------------------------------------------------------------------------------- /api/user/apps.py: -------------------------------------------------------------------------------- 1 | from django.apps import AppConfig 2 | 3 | 4 | class UserConfig(AppConfig): 5 | name = 'user' 6 | -------------------------------------------------------------------------------- /api/user/migrations/0001_initial.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.0.8 on 2020-11-10 05:48 2 | 3 | import django.contrib.auth.models 4 | from django.db import migrations, models 5 | import django.utils.timezone 6 | 7 | 8 | class Migration(migrations.Migration): 9 | 10 | initial = True 11 | 12 | dependencies = [ 13 | ('auth', '0011_update_proxy_permissions'), 14 | ] 15 | 16 | operations = [ 17 | migrations.CreateModel( 18 | name='CustomUser', 19 | fields=[ 20 | ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), 21 | ('password', models.CharField(max_length=128, verbose_name='password')), 22 | ('last_login', models.DateTimeField(blank=True, null=True, verbose_name='last login')), 23 | ('is_superuser', models.BooleanField(default=False, help_text='Designates that this user has all permissions without explicitly assigning them.', verbose_name='superuser status')), 24 | ('first_name', models.CharField(blank=True, max_length=30, verbose_name='first name')), 25 | ('last_name', models.CharField(blank=True, max_length=150, verbose_name='last name')), 26 | ('is_staff', models.BooleanField(default=False, help_text='Designates whether the user can log into this admin site.', verbose_name='staff status')), 27 | ('is_active', models.BooleanField(default=True, help_text='Designates whether this user should be treated as active. Unselect this instead of deleting accounts.', verbose_name='active')), 28 | ('date_joined', models.DateTimeField(default=django.utils.timezone.now, verbose_name='date joined')), 29 | ('name', models.CharField(default='Anonymous', max_length=50)), 30 | ('email', models.EmailField(max_length=254, unique=True)), 31 | ('phone', models.CharField(blank=True, max_length=20, null=True)), 32 | ('gender', models.CharField(blank=True, max_length=10, null=True)), 33 | ('session_token', models.CharField(default=0, max_length=10)), 34 | ('created_at', models.DateTimeField(auto_now_add=True)), 35 | ('updated_at', models.DateTimeField(auto_now=True)), 36 | ('groups', models.ManyToManyField(blank=True, help_text='The groups this user belongs to. A user will get all permissions granted to each of their groups.', related_name='user_set', related_query_name='user', to='auth.Group', verbose_name='groups')), 37 | ('user_permissions', models.ManyToManyField(blank=True, help_text='Specific permissions for this user.', related_name='user_set', related_query_name='user', to='auth.Permission', verbose_name='user permissions')), 38 | ], 39 | options={ 40 | 'verbose_name': 'user', 41 | 'verbose_name_plural': 'users', 42 | 'abstract': False, 43 | }, 44 | managers=[ 45 | ('objects', django.contrib.auth.models.UserManager()), 46 | ], 47 | ), 48 | ] 49 | -------------------------------------------------------------------------------- /api/user/migrations/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mahmud-sajib/Django-Ecommerce-API/36d42f67a08ab626150b6ed7e7e952b0ee68d581/api/user/migrations/__init__.py -------------------------------------------------------------------------------- /api/user/models.py: -------------------------------------------------------------------------------- 1 | from django.db import models 2 | from django.contrib.auth.models import AbstractUser 3 | 4 | # Create your models here. 5 | 6 | 7 | class CustomUser(AbstractUser): 8 | 9 | name = models.CharField(max_length=50, default='Anonymous') 10 | email = models.EmailField(max_length=254, unique=True) 11 | username = None 12 | 13 | USERNAME_FIELD = 'email' 14 | REQUIRED_FIELDS = [] 15 | 16 | phone = models.CharField(max_length=20, blank=True, null=True) 17 | gender = models.CharField(max_length=10, blank=True, null=True) 18 | session_token = models.CharField(max_length=10, default=0) 19 | created_at = models.DateTimeField(auto_now_add=True) 20 | updated_at = models.DateTimeField(auto_now=True) 21 | -------------------------------------------------------------------------------- /api/user/serializers.py: -------------------------------------------------------------------------------- 1 | from rest_framework import serializers 2 | from django.contrib.auth.hashers import make_password 3 | from rest_framework.decorators import authentication_classes, permission_classes 4 | 5 | from .models import CustomUser 6 | 7 | class UserSerializer(serializers.ModelSerializer): 8 | 9 | # Create User 10 | def create(self, validated_data): 11 | # Extract the password 12 | password = validated_data.pop('password', None) 13 | 14 | # Assign `CustomUser` model's data to `instance` object 15 | instance = self.Meta.model(**validated_data) 16 | 17 | if password is not None: 18 | instance.set_password(password) 19 | 20 | instance.save() 21 | return instance 22 | 23 | # Update User 24 | def update(self, instance, validated_data): 25 | for attr, value in validated_data.items(): 26 | if attr == 'password': 27 | instance.set_password(value) # Apply `set_password` function 28 | else: 29 | setattr(instance, attr, value) # Update the instance with new value 30 | 31 | instance.save() 32 | return instance 33 | 34 | class Meta: 35 | # Model to be serialized 36 | model = CustomUser 37 | 38 | # Fields to be serialized 39 | fields = ('name', 'email', 'password', 'phone', 'gender', 'is_active', 'is_staff', 'is_superuser') 40 | 41 | # Make the password inaccessible in GET request and don't show in plain text 42 | extra_kwargs = { 43 | 'password':{ 44 | 'write_only':True, 45 | 'style':{'input_type':'password'} 46 | } 47 | } -------------------------------------------------------------------------------- /api/user/tests.py: -------------------------------------------------------------------------------- 1 | from django.test import TestCase 2 | 3 | # Create your tests here. 4 | -------------------------------------------------------------------------------- /api/user/urls.py: -------------------------------------------------------------------------------- 1 | from rest_framework import routers 2 | from django.urls import path, include 3 | from . import views 4 | 5 | router = routers.DefaultRouter() 6 | router.register(r'', views.UserViewSet) 7 | 8 | urlpatterns = [ 9 | path('login/', views.signin, name='signin'), 10 | path('logout//', views.signout, name='signout'), 11 | path('', include(router.urls)), 12 | ] 13 | -------------------------------------------------------------------------------- /api/user/views.py: -------------------------------------------------------------------------------- 1 | from rest_framework import viewsets 2 | from rest_framework.permissions import AllowAny 3 | from .serializers import UserSerializer 4 | from .models import CustomUser 5 | from django.http import JsonResponse 6 | from django.contrib.auth import get_user_model 7 | from django.views.decorators.csrf import csrf_exempt 8 | from django.contrib.auth import login, logout 9 | 10 | import random 11 | import re 12 | 13 | # Create your views here. 14 | 15 | # Generate session view 16 | def generate_session_token(length=10): 17 | char_list = [chr(i) for i in range(97, 123)] 18 | int_list = [str(i) for i in range(10)] 19 | 20 | # Create a unique token 21 | return ''.join(random.SystemRandom().choice(char_list + int_list) for _ in range(length)) 22 | 23 | # Login view 24 | @csrf_exempt # Allow CSRF 25 | def signin(request): 26 | if not request.method == 'POST': 27 | return JsonResponse({'error':"You are not eligible for login"}) 28 | 29 | username = request.POST['email'] 30 | password = request.POST['password'] 31 | 32 | if not re.match("^[\w\.\+\-]+\@[\w]+\.[a-z]{2,3}$", username): 33 | return JsonResponse({'error':"Enter a valid Email"}) 34 | 35 | if len(password) < 6: 36 | return JsonResponse({'error':"Password must be 6 character long"}) 37 | 38 | UserModel = get_user_model() 39 | 40 | try: 41 | user = UserModel.objects.get(email=username) 42 | 43 | if user.check_password(password): 44 | usr_dict = UserModel.objects.filter(email=username).values().first() 45 | usr_dict.pop('password') # Extract Password so it doesn't travel to frontend 46 | 47 | # If session_token is not 0 it's already running (user is logged in) 48 | if user.session_token != '0': 49 | # If user is not logged in we set session_token to 0 50 | user.session_token = '0' 51 | # Save session 52 | user.save() 53 | return JsonResponse({'error':"Previous session exists"}) 54 | 55 | # Generate session token 56 | token = generate_session_token() 57 | user.session_token = token 58 | # Save session 59 | user.save() 60 | # Log user in 61 | login (request, user) 62 | # Return session token along with user attributes 63 | return JsonResponse({'token': token, 'user': usr_dict}) 64 | else: 65 | return JsonResponse({'token': 'Invalid password'}) 66 | 67 | except UserModel.DoesNotExist: 68 | return JsonResponse({'error': 'Invalid Email'}) 69 | 70 | # Logout view 71 | def signout(request, id): 72 | 73 | UserModel = get_user_model() 74 | 75 | try: 76 | user = UserModel.objects.get(pk=id) 77 | user.session_token = "0" 78 | user.save() 79 | logout(request) 80 | 81 | except UserModel.DoesNotExist: 82 | return JsonResponse({'error': 'Invalid User ID'}) 83 | 84 | return JsonResponse({'success': 'Logout successful'}) 85 | 86 | # User Permission View 87 | class UserViewSet(viewsets.ModelViewSet): 88 | permission_classes_by_action = {'create' : [AllowAny]} 89 | queryset = CustomUser.objects.all().order_by('id') 90 | serializer_class = UserSerializer 91 | 92 | def get_permissions(self): 93 | try: 94 | # Return permission_classes depending on `action` 95 | return [permission() for permission in self.permission_classes_by_action[self.action]] 96 | 97 | except KeyError: 98 | # If action is not set return default permission_classes 99 | return [permission() for permission in self.permission_classes] 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | -------------------------------------------------------------------------------- /api/views.py: -------------------------------------------------------------------------------- 1 | from django.shortcuts import render 2 | from django.http import JsonResponse 3 | 4 | 5 | # Create your views here. 6 | 7 | def home(request): 8 | return JsonResponse({'info':"Django React Ecommerce Project", 'developer':"Mahmud Sajib"}) 9 | 10 | 11 | -------------------------------------------------------------------------------- /db.json: -------------------------------------------------------------------------------- 1 | [{"model": "auth.permission", "pk": 1, "fields": {"name": "Can add log entry", "content_type": 1, "codename": "add_logentry"}}, {"model": "auth.permission", "pk": 2, "fields": {"name": "Can change log entry", "content_type": 1, "codename": "change_logentry"}}, {"model": "auth.permission", "pk": 3, "fields": {"name": "Can delete log entry", "content_type": 1, "codename": "delete_logentry"}}, {"model": "auth.permission", "pk": 4, "fields": {"name": "Can view log entry", "content_type": 1, "codename": "view_logentry"}}, {"model": "auth.permission", "pk": 5, "fields": {"name": "Can add permission", "content_type": 2, "codename": "add_permission"}}, {"model": "auth.permission", "pk": 6, "fields": {"name": "Can change permission", "content_type": 2, "codename": "change_permission"}}, {"model": "auth.permission", "pk": 7, "fields": {"name": "Can delete permission", "content_type": 2, "codename": "delete_permission"}}, {"model": "auth.permission", "pk": 8, "fields": {"name": "Can view permission", "content_type": 2, "codename": "view_permission"}}, {"model": "auth.permission", "pk": 9, "fields": {"name": "Can add group", "content_type": 3, "codename": "add_group"}}, {"model": "auth.permission", "pk": 10, "fields": {"name": "Can change group", "content_type": 3, "codename": "change_group"}}, {"model": "auth.permission", "pk": 11, "fields": {"name": "Can delete group", "content_type": 3, "codename": "delete_group"}}, {"model": "auth.permission", "pk": 12, "fields": {"name": "Can view group", "content_type": 3, "codename": "view_group"}}, {"model": "auth.permission", "pk": 13, "fields": {"name": "Can add content type", "content_type": 4, "codename": "add_contenttype"}}, {"model": "auth.permission", "pk": 14, "fields": {"name": "Can change content type", "content_type": 4, "codename": "change_contenttype"}}, {"model": "auth.permission", "pk": 15, "fields": {"name": "Can delete content type", "content_type": 4, "codename": "delete_contenttype"}}, {"model": "auth.permission", "pk": 16, "fields": {"name": "Can view content type", "content_type": 4, "codename": "view_contenttype"}}, {"model": "auth.permission", "pk": 17, "fields": {"name": "Can add session", "content_type": 5, "codename": "add_session"}}, {"model": "auth.permission", "pk": 18, "fields": {"name": "Can change session", "content_type": 5, "codename": "change_session"}}, {"model": "auth.permission", "pk": 19, "fields": {"name": "Can delete session", "content_type": 5, "codename": "delete_session"}}, {"model": "auth.permission", "pk": 20, "fields": {"name": "Can view session", "content_type": 5, "codename": "view_session"}}, {"model": "auth.permission", "pk": 21, "fields": {"name": "Can add Token", "content_type": 6, "codename": "add_token"}}, {"model": "auth.permission", "pk": 22, "fields": {"name": "Can change Token", "content_type": 6, "codename": "change_token"}}, {"model": "auth.permission", "pk": 23, "fields": {"name": "Can delete Token", "content_type": 6, "codename": "delete_token"}}, {"model": "auth.permission", "pk": 24, "fields": {"name": "Can view Token", "content_type": 6, "codename": "view_token"}}, {"model": "auth.permission", "pk": 25, "fields": {"name": "Can add token", "content_type": 7, "codename": "add_tokenproxy"}}, {"model": "auth.permission", "pk": 26, "fields": {"name": "Can change token", "content_type": 7, "codename": "change_tokenproxy"}}, {"model": "auth.permission", "pk": 27, "fields": {"name": "Can delete token", "content_type": 7, "codename": "delete_tokenproxy"}}, {"model": "auth.permission", "pk": 28, "fields": {"name": "Can view token", "content_type": 7, "codename": "view_tokenproxy"}}, {"model": "auth.permission", "pk": 29, "fields": {"name": "Can add category", "content_type": 8, "codename": "add_category"}}, {"model": "auth.permission", "pk": 30, "fields": {"name": "Can change category", "content_type": 8, "codename": "change_category"}}, {"model": "auth.permission", "pk": 31, "fields": {"name": "Can delete category", "content_type": 8, "codename": "delete_category"}}, {"model": "auth.permission", "pk": 32, "fields": {"name": "Can view category", "content_type": 8, "codename": "view_category"}}, {"model": "auth.permission", "pk": 33, "fields": {"name": "Can add product", "content_type": 9, "codename": "add_product"}}, {"model": "auth.permission", "pk": 34, "fields": {"name": "Can change product", "content_type": 9, "codename": "change_product"}}, {"model": "auth.permission", "pk": 35, "fields": {"name": "Can delete product", "content_type": 9, "codename": "delete_product"}}, {"model": "auth.permission", "pk": 36, "fields": {"name": "Can view product", "content_type": 9, "codename": "view_product"}}, {"model": "auth.permission", "pk": 37, "fields": {"name": "Can add user", "content_type": 10, "codename": "add_customuser"}}, {"model": "auth.permission", "pk": 38, "fields": {"name": "Can change user", "content_type": 10, "codename": "change_customuser"}}, {"model": "auth.permission", "pk": 39, "fields": {"name": "Can delete user", "content_type": 10, "codename": "delete_customuser"}}, {"model": "auth.permission", "pk": 40, "fields": {"name": "Can view user", "content_type": 10, "codename": "view_customuser"}}, {"model": "auth.permission", "pk": 41, "fields": {"name": "Can add order", "content_type": 11, "codename": "add_order"}}, {"model": "auth.permission", "pk": 42, "fields": {"name": "Can change order", "content_type": 11, "codename": "change_order"}}, {"model": "auth.permission", "pk": 43, "fields": {"name": "Can delete order", "content_type": 11, "codename": "delete_order"}}, {"model": "auth.permission", "pk": 44, "fields": {"name": "Can view order", "content_type": 11, "codename": "view_order"}}, {"model": "sessions.session", "pk": "y9ax7cytjs3z23h6cj3mxf5bblc2v22y", "fields": {"session_data": "OWY3MGFhZTRiZTZlMzgyNmQ4OTRkMjUyOWM4YjA1YTA2NmY3Y2MyZjp7Il9hdXRoX3VzZXJfaWQiOiIxIiwiX2F1dGhfdXNlcl9iYWNrZW5kIjoiZGphbmdvLmNvbnRyaWIuYXV0aC5iYWNrZW5kcy5Nb2RlbEJhY2tlbmQiLCJfYXV0aF91c2VyX2hhc2giOiIwODllNzNmNmYzODQwMGNhYmRlZWIyMzAxNzZjNDJhYjhmNDc2YTYwIn0=", "expire_date": "2020-11-28T05:27:59.543Z"}}, {"model": "user.customuser", "pk": 1, "fields": {"password": "pbkdf2_sha256$180000$qIyUPaJXEaIz$uQJTfpNh6EQ7LfPGIG4F9JSl3gHFXB+6O7mSuLraKSs=", "last_login": "2020-11-14T05:27:59.447Z", "is_superuser": true, "first_name": "", "last_name": "", "is_staff": true, "is_active": true, "date_joined": "2020-11-14T05:26:21.038Z", "name": "mahmud", "email": "sajibforest@gmail.com", "phone": "01553848755", "gender": "Male", "session_token": "0", "created_at": "2020-11-14T05:26:21.546Z", "updated_at": "2020-11-14T05:26:21.546Z", "groups": [], "user_permissions": []}}] -------------------------------------------------------------------------------- /ecommerce/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mahmud-sajib/Django-Ecommerce-API/36d42f67a08ab626150b6ed7e7e952b0ee68d581/ecommerce/__init__.py -------------------------------------------------------------------------------- /ecommerce/asgi.py: -------------------------------------------------------------------------------- 1 | """ 2 | ASGI config for ecommerce project. 3 | 4 | It exposes the ASGI callable as a module-level variable named ``application``. 5 | 6 | For more information on this file, see 7 | https://docs.djangoproject.com/en/3.0/howto/deployment/asgi/ 8 | """ 9 | 10 | import os 11 | 12 | from django.core.asgi import get_asgi_application 13 | 14 | os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'ecommerce.settings') 15 | 16 | application = get_asgi_application() 17 | -------------------------------------------------------------------------------- /ecommerce/settings.py: -------------------------------------------------------------------------------- 1 | """ 2 | Django settings for ecommerce project. 3 | 4 | Generated by 'django-admin startproject' using Django 3.0.8. 5 | 6 | For more information on this file, see 7 | https://docs.djangoproject.com/en/3.0/topics/settings/ 8 | 9 | For the full list of settings and their values, see 10 | https://docs.djangoproject.com/en/3.0/ref/settings/ 11 | """ 12 | 13 | import os 14 | import django_heroku 15 | 16 | # Build paths inside the project like this: os.path.join(BASE_DIR, ...) 17 | BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) 18 | 19 | 20 | # Quick-start development settings - unsuitable for production 21 | # See https://docs.djangoproject.com/en/3.0/howto/deployment/checklist/ 22 | 23 | # SECURITY WARNING: keep the secret key used in production secret! 24 | SECRET_KEY = '81z2%e(-*1w=561dt195n8+my=fzz5o%=z2811p@ywu0^w7242' 25 | 26 | # SECURITY WARNING: don't run with debug turned on in production! 27 | DEBUG = True 28 | 29 | ALLOWED_HOSTS = ['ecom-rest.herokuapp.com'] 30 | 31 | 32 | # Application definition 33 | 34 | INSTALLED_APPS = [ 35 | 'django.contrib.admin', 36 | 'django.contrib.auth', 37 | 'django.contrib.contenttypes', 38 | 'django.contrib.sessions', 39 | 'django.contrib.messages', 40 | 'django.contrib.staticfiles', 41 | 'corsheaders', 42 | 'rest_framework', 43 | 'rest_framework.authtoken', 44 | 'drf_yasg', 45 | 'api', 46 | 'api.category', 47 | 'api.product', 48 | 'api.user', 49 | 'api.order', 50 | 'api.payment', 51 | ] 52 | 53 | MIDDLEWARE = [ 54 | 'corsheaders.middleware.CorsMiddleware', 55 | 'django.middleware.security.SecurityMiddleware', 56 | 'django.contrib.sessions.middleware.SessionMiddleware', 57 | 'django.middleware.common.CommonMiddleware', 58 | 'django.middleware.csrf.CsrfViewMiddleware', 59 | 'django.contrib.auth.middleware.AuthenticationMiddleware', 60 | 'django.contrib.messages.middleware.MessageMiddleware', 61 | 'django.middleware.clickjacking.XFrameOptionsMiddleware', 62 | ] 63 | 64 | ROOT_URLCONF = 'ecommerce.urls' 65 | 66 | TEMPLATES = [ 67 | { 68 | 'BACKEND': 'django.template.backends.django.DjangoTemplates', 69 | 'DIRS': [], 70 | 'APP_DIRS': True, 71 | 'OPTIONS': { 72 | 'context_processors': [ 73 | 'django.template.context_processors.debug', 74 | 'django.template.context_processors.request', 75 | 'django.contrib.auth.context_processors.auth', 76 | 'django.contrib.messages.context_processors.messages', 77 | ], 78 | }, 79 | }, 80 | ] 81 | 82 | WSGI_APPLICATION = 'ecommerce.wsgi.application' 83 | 84 | 85 | # Database 86 | # https://docs.djangoproject.com/en/3.0/ref/settings/#databases 87 | 88 | DATABASES = { 89 | 'default': { 90 | 'ENGINE': 'django.db.backends.sqlite3', 91 | 'NAME': os.path.join(BASE_DIR, 'db.sqlite3'), 92 | } 93 | } 94 | 95 | 96 | # Password validation 97 | # https://docs.djangoproject.com/en/3.0/ref/settings/#auth-password-validators 98 | 99 | AUTH_PASSWORD_VALIDATORS = [ 100 | { 101 | 'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator', 102 | }, 103 | { 104 | 'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator', 105 | }, 106 | { 107 | 'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator', 108 | }, 109 | { 110 | 'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator', 111 | }, 112 | ] 113 | 114 | 115 | # Internationalization 116 | # https://docs.djangoproject.com/en/3.0/topics/i18n/ 117 | 118 | LANGUAGE_CODE = 'en-us' 119 | 120 | TIME_ZONE = 'UTC' 121 | 122 | USE_I18N = True 123 | 124 | USE_L10N = True 125 | 126 | USE_TZ = True 127 | 128 | 129 | # Static files (CSS, JavaScript, Images) 130 | # https://docs.djangoproject.com/en/3.0/howto/static-files/ 131 | 132 | STATIC_ROOT = os.path.join(BASE_DIR, 'staticfiles') 133 | STATIC_URL = '/static/' 134 | 135 | 136 | # Location that holds user-uploaded files. 137 | MEDIA_ROOT = os.path.join(BASE_DIR, 'static/images') 138 | 139 | # URL that handles the media files served from MEDIA_ROOT. 140 | MEDIA_URL = '/images/' 141 | 142 | AUTH_USER_MODEL = "user.CustomUser" 143 | 144 | django_heroku.settings(locals()) 145 | 146 | # Cors header config: 147 | CORS_ALLOW_ALL_ORIGINS = True 148 | 149 | # REST FRAMEWORK config: 150 | REST_FRAMEWORK = { 151 | # Use Django's standard `django.contrib.auth` permissions, 152 | # or allow read-only access for unauthenticated users. 153 | 'DEFAULT_PERMISSION_CLASSES': [ 154 | 'rest_framework.permissions.DjangoModelPermissionsOrAnonReadOnly' 155 | ], 156 | 'DEFAULT_AUTHENTICATION_CLASSES': [ 157 | 'rest_framework.authentication.BasicAuthentication', 158 | 'rest_framework.authentication.SessionAuthentication', 159 | 'rest_framework.authentication.TokenAuthentication', 160 | ] 161 | } 162 | -------------------------------------------------------------------------------- /ecommerce/urls.py: -------------------------------------------------------------------------------- 1 | """ecommerce URL Configuration 2 | 3 | The `urlpatterns` list routes URLs to views. For more information please see: 4 | https://docs.djangoproject.com/en/3.0/topics/http/urls/ 5 | Examples: 6 | Function views 7 | 1. Add an import: from my_app import views 8 | 2. Add a URL to urlpatterns: path('', views.home, name='home') 9 | Class-based views 10 | 1. Add an import: from other_app.views import Home 11 | 2. Add a URL to urlpatterns: path('', Home.as_view(), name='home') 12 | Including another URLconf 13 | 1. Import the include() function: from django.urls import include, path 14 | 2. Add a URL to urlpatterns: path('blog/', include('blog.urls')) 15 | """ 16 | from django.contrib import admin 17 | from django.urls import path, include 18 | 19 | from django.conf.urls.static import static 20 | from django.conf import settings 21 | 22 | from rest_framework import permissions 23 | from drf_yasg.views import get_schema_view 24 | from drf_yasg import openapi 25 | 26 | schema_view = get_schema_view( 27 | openapi.Info( 28 | title="Ecommerce Rest API", 29 | default_version='v1', 30 | description="A Python/Django API to build E-commerce stores!", 31 | # terms_of_service="https://www.google.com/policies/terms/", 32 | # contact=openapi.Contact(email="contact@snippets.local"), 33 | # license=openapi.License(name="BSD License"), 34 | ), 35 | public=True, 36 | permission_classes=(permissions.AllowAny,), 37 | ) 38 | 39 | 40 | urlpatterns = [ 41 | # path('api-documentation/', schema_view), 42 | path('', schema_view.with_ui('swagger', cache_timeout=0), name='schema-swagger-ui'), 43 | path('admin/', admin.site.urls), 44 | path('api-auth/', include('rest_framework.urls')), 45 | path('api/', include('api.urls')), 46 | ] 47 | 48 | urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT) -------------------------------------------------------------------------------- /ecommerce/wsgi.py: -------------------------------------------------------------------------------- 1 | """ 2 | WSGI config for ecommerce project. 3 | 4 | It exposes the WSGI callable as a module-level variable named ``application``. 5 | 6 | For more information on this file, see 7 | https://docs.djangoproject.com/en/3.0/howto/deployment/wsgi/ 8 | """ 9 | 10 | import os 11 | 12 | from django.core.wsgi import get_wsgi_application 13 | 14 | os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'ecommerce.settings') 15 | 16 | application = get_wsgi_application() 17 | -------------------------------------------------------------------------------- /manage.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | """Django's command-line utility for administrative tasks.""" 3 | import os 4 | import sys 5 | 6 | 7 | def main(): 8 | os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'ecommerce.settings') 9 | try: 10 | from django.core.management import execute_from_command_line 11 | except ImportError as exc: 12 | raise ImportError( 13 | "Couldn't import Django. Are you sure it's installed and " 14 | "available on your PYTHONPATH environment variable? Did you " 15 | "forget to activate a virtual environment?" 16 | ) from exc 17 | execute_from_command_line(sys.argv) 18 | 19 | 20 | if __name__ == '__main__': 21 | main() 22 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | asgiref==3.3.1 2 | braintree==4.5.0 3 | certifi==2020.11.8 4 | chardet==3.0.4 5 | coreapi==2.3.3 6 | coreschema==0.0.4 7 | dj-database-url==0.5.0 8 | Django==3.0.8 9 | django-cors-headers==3.5.0 10 | django-heroku==0.3.1 11 | djangorestframework==3.12.2 12 | drf-yasg==1.20.0 13 | gunicorn==20.0.4 14 | idna==2.10 15 | inflection==0.5.1 16 | itypes==1.2.0 17 | Jinja2==2.11.2 18 | MarkupSafe==1.1.1 19 | packaging==20.4 20 | Pillow==8.0.1 21 | psycopg2==2.8.6 22 | pyparsing==2.4.7 23 | pytz==2020.4 24 | requests==2.25.0 25 | ruamel.yaml==0.16.12 26 | ruamel.yaml.clib==0.2.2 27 | six==1.15.0 28 | sqlparse==0.4.1 29 | uritemplate==3.0.1 30 | urllib3==1.26.2 31 | whitenoise==5.2.0 32 | --------------------------------------------------------------------------------