Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions ecommerce-backend-master/ecommerce-backend-master/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
FROM python:3
WORKDIR /usr/src/app
ADD requirements.txt /usr/src/app
RUN pip install -r requirements.txt
ADD . /usr/src/app
2 changes: 2 additions & 0 deletions ecommerce-backend-master/ecommerce-backend-master/Procfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
release: python manage.py migrate --settings=src.settings.production
web: gunicorn src.wsgi --log-file -
77 changes: 77 additions & 0 deletions ecommerce-backend-master/ecommerce-backend-master/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
# ecommerce-backend
Django backend for eCommerce project

This project is built using Django REST Framework to provide the backend API for eCommerce project. The deployed API is available on [Heroku](https://ecommerce-backend-django.herokuapp.com/). The frontend is available [here](https://github.com/ncunx/ecommerce-frontend).

Features
--------
1. Products API endpoint available at `/api/products/`.
2. Custom user authentication using JSON Web Tokens. The API is available at `/api/accounts/`.
2. Simple newsletter functionality: superuser can view the list of all subscribers in Django admin panel; any visitor can subscribe. The relevant API endpoint is available at `/api/newsletter/`.
3. [Stripe](https://stripe.com/) payments API endpoint available at `/api/payments/`.

Main requirements
------------

1. `python` 3.5, 3.6, 3.7
2. `Django` 2.1.8
3. `PostgreSQL` 11.1

This project also uses other packages (see `requirements.txt` file for details).
For instance, tag support is provided by [django-taggit](https://github.com/alex/django-taggit) and image processing is thanks to [Pillow](https://github.com/python-pillow/Pillow).

## How to set up

### Setup using Docker

The easiest way to get backend up and running is via [Docker](https://www.docker.com/). See [docs](https://docs.docker.com/get-started/) to get started. Once set up run the following command:

`docker-compose up`

This command takes care of populating products list with sample data.

It may take a while for the process to complete, as Docker needs to pull required dependencies. Once it is done, the application should be accessible at `0.0.0.0:8000`.

### Manual setup

Firstly, create a new directory and change to it:

`mkdir ecommerce-backend && cd ecommerce-backend`

Then, clone this repository to the current directory:

`git clone https://github.com/ncunx/ecommerce-backend.git .`


For the backend to work, one needs to setup database like SQLite or PostgreSQL on a local machine. This project uses PostgreSQL by default (see [Django documentation](https://docs.djangoproject.com/en/2.1/ref/settings/#databases) for different setup). This process may vary from one OS to another, eg. on Arch Linux one can follow a straightforward guide [here](https://wiki.archlinux.org/index.php/PostgreSQL).

The database settings are specified in `src/settings/local.py`. In particular the default database name is `eCommerceDjango`, which can be created from the PostgreSQL shell by running `createdb eCommerceDjango`.

Next, set up a virtual environment and activate it:

`python3 -m venv env && source env/bin/activate`

Install required packages:

`pip3 install -r requirements.txt`

Next, perform migration:

`python3 manage.py migrate --settings=src.settings.local`

At this point, one may want to create a superuser account and create some products. One can also use sample data provided in `products/fixtures.json` by running:

`python3 manage.py loaddata products/fixture.json --settings=src.settings.local`

The backend is now ready. Run a local server with

`python3 manage.py runserver --settings=src.settings.local`

The backend should be available at `localhost:8000`.

In order to use [Stripe payments](https://stripe.com/) one needs to create an account and obtain a pair of keys (available in the dashboard after signing in). These keys should replace `STRIPE_SECRET_KEY` and `STRIPE_PUBLISHABLE_KEY` values in `src/settings/local.py`.

## To do

1. Implement an API endpoint at `/api/orders/` to accept POST request with order JSON from authenticated user, and GET requests from superuser (for further processing).
2. Implement password recovery.
Empty file.
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
from django.contrib import admin
from django.contrib.auth.admin import UserAdmin

from .forms import CustomUserCreationForm, CustomUserChangeForm
from .models import CustomUser


class CustomUserAdmin(UserAdmin):
add_form = CustomUserCreationForm
form = CustomUserChangeForm
model = CustomUser
list_display = ("first_name", "last_name", "email", "is_staff", "is_active")
list_filter = ("first_name", "last_name", "email", "is_staff", "is_active")
fieldsets = (
(None, {"fields": ("email", "password")}),
("Personal information", {"fields": ("first_name", "last_name")}),
("Permissions", {"fields": ("is_staff", "is_active")}),
)
add_fieldsets = (
(
None,
{
"classes": ("wide",),
"fields": ("email", "password1", "password2", "is_staff", "is_active"),
},
),
)
search_fields = ("first_name", "last_name", "email")
ordering = ("first_name", "last_name", "email")


admin.site.register(CustomUser, CustomUserAdmin)
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
from django.contrib.auth import get_user_model
from rest_framework import serializers
from rest_auth.serializers import LoginSerializer

try:
from allauth.utils import email_address_exists
from allauth.account.adapter import get_adapter
from allauth.account.utils import setup_user_email
except ImportError:
raise ImportError("allauth needs to be added to INSTALLED_APPS.")

User = get_user_model()


class CustomUserDetailsSerializer(serializers.ModelSerializer):
class Meta:
model = User
fields = ("id", "first_name", "last_name", "email")


class CustomLoginSerializer(LoginSerializer):
username = None
email = serializers.EmailField(required=False, allow_blank=True)
password = serializers.CharField(style={"input_type": "password"})


class CustomRegisterSerializer(serializers.Serializer):
"""
Modified RegisterSerializer class from rest_auth
"""

username = None
first_name = serializers.CharField(required=True)
last_name = serializers.CharField(required=True)
email = serializers.EmailField(required=True)
password1 = serializers.CharField(write_only=True, style={"input_type": "password"})
password2 = serializers.CharField(write_only=True, style={"input_type": "password"})

def validate_email(self, email):
email = get_adapter().clean_email(email)
if email and email_address_exists(email):
raise serializers.ValidationError(
"A user is already registered with this e-mail address."
)
return email

def validate_password1(self, password):
return get_adapter().clean_password(password)

def validate(self, data):
if data["password1"] != data["password2"]:
raise serializers.ValidationError("The two password fields didn't match.")
return data

def get_cleaned_data(self):
return {
"first_name": self.validated_data.get("first_name", ""),
"last_name": self.validated_data.get("last_name", ""),
"password1": self.validated_data.get("password1", ""),
"email": self.validated_data.get("email", ""),
}

def save(self, request):
adapter = get_adapter()
user = adapter.new_user(request)
self.cleaned_data = self.get_cleaned_data()
adapter.save_user(request, user, self)
setup_user_email(request, user, [])
return user
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
from django.urls import include, path
from .views import CustomRegisterView


urlpatterns = [
path("", include("rest_auth.urls")),
path("register/", include("rest_auth.registration.urls")),
]
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
from rest_auth.views import LoginView
from rest_auth.registration.views import RegisterView
from .serializers import CustomRegisterSerializer

class CustomLoginView(LoginView):
pass

class CustomRegisterView(RegisterView):
serializer_class = CustomRegisterSerializer
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
from django.apps import AppConfig


class AccountsConfig(AppConfig):
name = 'accounts'
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
from django.contrib.auth.forms import UserCreationForm, UserChangeForm
from .models import CustomUser


class CustomUserCreationForm(UserCreationForm):
class Meta(UserCreationForm):
model = CustomUser
fields = ("first_name", "last_name", "email")


class CustomUserChangeForm(UserChangeForm):
class Meta:
model = CustomUser
fields = ("first_name", "last_name", "email")
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
# Generated by Django 2.1.7 on 2019-06-13 18:16

from django.db import migrations, models
import django.utils.timezone


class Migration(migrations.Migration):

initial = True

dependencies = [
('auth', '0009_alter_user_last_name_max_length'),
]

operations = [
migrations.CreateModel(
name='CustomUser',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('password', models.CharField(max_length=128, verbose_name='password')),
('last_login', models.DateTimeField(blank=True, null=True, verbose_name='last login')),
('is_superuser', models.BooleanField(default=False, help_text='Designates that this user has all permissions without explicitly assigning them.', verbose_name='superuser status')),
('is_staff', models.BooleanField(default=False, help_text='Designates whether the user can log into this admin site.', verbose_name='staff status')),
('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')),
('date_joined', models.DateTimeField(default=django.utils.timezone.now, verbose_name='date joined')),
('first_name', models.CharField(max_length=255, verbose_name='First name')),
('last_name', models.CharField(max_length=255, verbose_name='Last name')),
('email', models.EmailField(max_length=254, unique=True)),
('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')),
('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')),
],
options={
'verbose_name': 'user',
'verbose_name_plural': 'users',
'abstract': False,
},
),
]
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
from django.contrib.auth.base_user import BaseUserManager
from django.contrib.auth.models import AbstractUser
from django.db import models


class CustomUserManager(BaseUserManager):
"""
Custom user model manager with email as the unique identifier
"""

def create_user(self, first_name, last_name, email, password, **extra_fields):
"""
Create user with the given email and password.
"""
if not email:
raise ValueError("The email must be set")
first_name = first_name.capitalize()
last_name = last_name.capitalize()
email = self.normalize_email(email)

user = self.model(
first_name=first_name, last_name=last_name, email=email, **extra_fields
)
user.set_password(password)
user.save()
return user

def create_superuser(self, first_name, last_name, email, password, **extra_fields):
"""
Create superuser with the given email and password.
"""
extra_fields.setdefault("is_staff", True)
extra_fields.setdefault("is_superuser", True)
extra_fields.setdefault("is_active", True)

if extra_fields.get("is_staff") is not True:
raise ValueError("Superuser must have is_staff=True.")
if extra_fields.get("is_superuser") is not True:
raise ValueError("Superuser must have is_superuser=True.")
return self.create_user(first_name, last_name, email, password, **extra_fields)


class CustomUser(AbstractUser):
username = None
first_name = models.CharField(max_length=255, verbose_name="First name")
last_name = models.CharField(max_length=255, verbose_name="Last name")
email = models.EmailField(unique=True)

USERNAME_FIELD = "email"
REQUIRED_FIELDS = ["first_name", "last_name"]

objects = CustomUserManager()

def __str__(self):
return self.email
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
from django.test import TestCase

# Create your tests here.
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
from django.shortcuts import render

# Create your views here.
Empty file.
8 changes: 8 additions & 0 deletions ecommerce-backend-master/ecommerce-backend-master/api/urls.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
from django.urls import path, include

urlpatterns = [
path("accounts/", include("accounts.api.urls")),
path("products/", include("products.api.urls")),
path("payments/", include("payments.urls")),
path("subscribers/", include("newsletter.api.urls")),
]
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
version: '3'

services:
db:
image: postgres
migration:
build: .
command: python3 manage.py migrate --settings=src.settings.local-docker
volumes:
- .:/usr/src/app
depends_on:
- db
load_fixtures:
build: .
command: python3 manage.py loaddata products/fixture.json --settings=src.settings.local-docker
volumes:
- .:/usr/src/app
depends_on:
- migration
web:
build: .
command: python3 manage.py runserver 0.0.0.0:8000 --settings=src.settings.local-docker
volumes:
- .:/usr/src/app
ports:
- "8000:8000"
depends_on:
- db
- migration
- load_fixtures
15 changes: 15 additions & 0 deletions ecommerce-backend-master/ecommerce-backend-master/manage.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
#!/usr/bin/env python
import os
import sys

if __name__ == '__main__':
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'src.settings')
try:
from django.core.management import execute_from_command_line
except ImportError as exc:
raise ImportError(
"Couldn't import Django. Are you sure it's installed and "
"available on your PYTHONPATH environment variable? Did you "
"forget to activate a virtual environment?"
) from exc
execute_from_command_line(sys.argv)
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Empty file.
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
from django.contrib import admin
from .models import Subscriber

# Register your models here.
class SubscriberAdmin(admin.ModelAdmin):
list_display = ('email', 'joined_date', )

admin.site.register(Subscriber, SubscriberAdmin)
Loading