20 |
WonderShop
21 |
23 |
24 |
25 |
26 |
51 |
57 |
58 |
62 |
72 |
73 |
74 |
75 | {% if messages %}
76 | {% for message in messages %}
77 |
78 | {{ message|safe }}
79 |
80 | ×
81 |
82 |
83 | {% endfor %}
84 | {% endif %}
85 |
86 | {% block body %}
87 | {% endblock %}
88 |
89 |
90 |
91 |
92 |
95 |
96 |
100 |
101 | {% block js %}
102 | {% endblock %}
103 |
104 |
--------------------------------------------------------------------------------
/firstapp/templates/firstapp/listproducts.html:
--------------------------------------------------------------------------------
1 | {% extends 'firstapp/basic.html' %}
2 | {% load hosts %}
3 | {% load static %}
4 | {% load myfilters %}
5 | {% block title %}
6 | {% endblock %}
7 | {% block css %}
8 |
9 |
34 | {% endblock %}
35 |
36 | {% block body%}
37 |
38 |
39 |
40 |
41 |
46 |
47 |
48 |
49 | Price
50 | less than 500
51 | less than 1000
52 | less than 2000
53 | less than 10000
54 |
55 |
56 |
57 |
58 | Sort
59 | Price: Low To High
60 | Price: High To Low
61 | Product Name
62 | What's New
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 | {% for i in product %}
71 |
72 |
73 |
74 |
75 |
76 |
{{i.price}}
77 |
78 |
Add To Cart
79 |
80 |
81 | {% endfor %}
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 | {% if is_paginated %}
91 |
115 | {% endif %}
116 |
117 |
118 |
119 |
120 |
121 | {% endblock %}
122 |
123 | {% block js %}
124 |
163 |
164 |
165 |
166 |
167 |
176 | {% endblock %}
--------------------------------------------------------------------------------
/firstproject/settings.py:
--------------------------------------------------------------------------------
1 | """
2 | Django settings for firstproject project.
3 |
4 | Generated by 'django-admin startproject' using Django 3.0.5.
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 environ
15 | env = environ.Env(
16 | # set casting, default value, if DEBUG=True/On in .env then True otherwise False
17 | DEBUG=(bool, False)
18 | )
19 | # reading .env file
20 | environ.Env.read_env()
21 |
22 | # Build paths inside the project like this: os.path.join(BASE_DIR, ...)
23 | BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
24 |
25 |
26 |
27 |
28 | # Quick-start development settings - unsuitable for production
29 | # See https://docs.djangoproject.com/en/3.0/howto/deployment/checklist/
30 |
31 | # SECURITY WARNING: keep the secret key used in production secret!
32 | SECRET_KEY = 'f%dk29k%3z7c1!&1587(2gv=@-xao^ko&0s=@sg8=fembuf)8a'
33 |
34 | # SECURITY WARNING: don't run with debug turned on in production!
35 | DEBUG = True
36 |
37 | ALLOWED_HOSTS = ['www.wondershop.in', 'services.wondershop.in', '127.0.0.1', 'admin.wondershop.in']
38 |
39 |
40 | # Application definition
41 |
42 | INSTALLED_APPS = [
43 | 'django.contrib.admin',
44 | 'django.contrib.auth',
45 | 'django.contrib.contenttypes',
46 | 'django.contrib.sessions',
47 | 'django.contrib.messages',
48 | 'django.contrib.staticfiles',
49 | #'django.contrib.sites',
50 |
51 | 'firstapp',
52 | 'seller',
53 | 'django_hosts',
54 |
55 | #'channels',
56 |
57 | #Gunicorn development server
58 | #'dj_static',
59 | ]
60 |
61 | # CACHES = {
62 | # 'default': {
63 | # 'BACKEND': 'django.core.cache.backends.locmem.LocMemCache',
64 | # 'LOCATION': 'my_cache_table',
65 | # }
66 | # }
67 |
68 | #SESSION_ENGINE = "django.contrib.sessions.backends.signed_cookies"
69 | #SESSION_ENGINE = "django.contrib.sessions.backends.file"
70 | #SESSION_FILE_PATH = r"D:\Mastering Django\firstproject"
71 | #SESSION_ENGINE = "django.contrib.sessions.backends.cache"
72 | #SESSION_ENGINE = "django.contrib.sessions.backends.cached_db"
73 | #SESSION_CACHE_ALIAS
74 |
75 | MIDDLEWARE = [
76 | 'django_hosts.middleware.HostsRequestMiddleware',
77 | 'django.middleware.security.SecurityMiddleware',
78 | 'django.contrib.sessions.middleware.SessionMiddleware',
79 | 'django.middleware.common.CommonMiddleware',
80 | 'django.middleware.csrf.CsrfViewMiddleware',
81 | 'django.contrib.auth.middleware.AuthenticationMiddleware', #method_is_async = False
82 | 'django.contrib.messages.middleware.MessageMiddleware',
83 | 'django.middleware.clickjacking.XFrameOptionsMiddleware',
84 | 'django_hosts.middleware.HostsResponseMiddleware'
85 | ]
86 |
87 |
88 | # AUTHENTICATION_BACKENDS = (
89 | # 'firstapp.backends.MyAuthBackend', # our custom authentication backend
90 | # 'django.contrib.auth.backends.ModelBackend' # fallback to default authentication backend if first fails
91 | # )
92 |
93 | ROOT_URLCONF = 'firstproject.urls'
94 | ROOT_HOSTCONF = 'firstproject.hosts'
95 | DEFAULT_HOST = 'www'
96 | PARENT_HOST = 'wondershop.in'
97 | HOST_PORT = "8000"
98 |
99 | TEMPLATES = [
100 | {
101 | 'BACKEND': 'django.template.backends.django.DjangoTemplates',
102 | 'DIRS': [],
103 | 'APP_DIRS': True,
104 | 'OPTIONS': {
105 | 'context_processors': [
106 | 'django.template.context_processors.debug',
107 | 'django.template.context_processors.request',
108 | 'django.contrib.auth.context_processors.auth',
109 | 'django.contrib.messages.context_processors.messages',
110 | ],
111 | },
112 | },
113 | ]
114 |
115 | WSGI_APPLICATION = 'firstproject.wsgi.application'
116 |
117 | ASGI_APPLICATION = 'firstproject.asgi.application'
118 |
119 |
120 | # Database
121 | # https://docs.djangoproject.com/en/3.0/ref/settings/#databases
122 |
123 | DATABASES = {
124 | 'default': {
125 | 'ENGINE': 'django.db.backends.sqlite3',
126 | 'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
127 | },
128 | # 'default': {
129 | # 'ENGINE': 'django.db.backends.mysql',
130 | # 'NAME': 'TestDB',
131 | # 'USER': 'root',
132 | # 'PASSWORD': '',
133 | # 'HOST': '127.0.0.1',
134 | # 'PORT': '3306',
135 | # }
136 | }
137 |
138 |
139 | # Password validation
140 | # https://docs.djangoproject.com/en/3.0/ref/settings/#auth-password-validators
141 |
142 | AUTH_PASSWORD_VALIDATORS = [
143 | {
144 | 'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator',
145 | },
146 | {
147 | 'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator',
148 | },
149 | {
150 | 'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator',
151 | },
152 | {
153 | 'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator',
154 | },
155 | ]
156 |
157 |
158 | # Internationalization
159 | # https://docs.djangoproject.com/en/3.0/topics/i18n/
160 |
161 | LANGUAGE_CODE = 'en-us'
162 |
163 | TIME_ZONE = 'UTC'
164 |
165 | USE_I18N = True
166 |
167 | USE_L10N = True
168 |
169 | USE_TZ = True
170 |
171 |
172 | # Static files (CSS, JavaScript, Images)
173 | # https://docs.djangoproject.com/en/3.0/howto/static-files/
174 |
175 |
176 | STATIC_URL = '/static/'
177 | STATIC_ROOT = os.path.join(BASE_DIR , 'static_cdn') # when deployemnt do collectstatic command and then in static file settings in web server(apache/nginx) mention alias as '/static' after it path of static_cdn folder or static folder as set by you
178 | MEDIA_URL = '/media/'
179 | MEDIA_ROOT = os.path.join(BASE_DIR , 'media')
180 | STATICFILES_DIRS = [
181 | os.path.join(BASE_DIR, "static"), # mention folders in saticfiles_dirs where django has to look for static files except static folder inside apps during development
182 | ]
183 |
184 |
185 |
186 | AUTH_USER_MODEL = 'firstapp.CustomUser'
187 |
188 |
189 | LOGIN_URL = 'login'
190 | LOGIN_REDIRECT_URL = 'index'
191 | LOGOUT_REDIRECT_URL = 'index'
192 |
193 | from django.contrib.messages import constants as messages
194 | MESSAGE_TAGS = {
195 | messages.ERROR : 'danger' # here we can override tags meaning in place of there actual color replace by another
196 | }
197 |
198 |
199 | #SMTP SETTINGS
200 | EMAIL_BACKEND = "django.core.mail.backends.smtp.EmailBackend"
201 | EMAIL_USE_TLS = True
202 | EMAIL_HOST = "smtp.gmail.com"
203 | EMAIL_PORT = 587
204 | EMAIL_HOST_USER ='priyanshuguptacontact@gmail.com'
205 | EMAIL_HOST_PASSWORD = env('EMAIL_HOST_PASSWORD')
206 | DEFAULT_FROM_EMAIL = 'Testing
'
207 |
208 | #PASSWORD_RESET_TIMEOUT_DAYS = "7"
209 |
210 |
211 | # PAYMENT GATEWAY SETTINGS
212 | razorpay_id = env('RAZORPAY_ID')
213 | razorpay_account_id = env('razorpay_account_id')
214 |
215 |
216 |
--------------------------------------------------------------------------------
/firstapp/admin.py:
--------------------------------------------------------------------------------
1 | from django.contrib import admin
2 | from django.contrib.auth.admin import UserAdmin
3 | from django.contrib.auth.models import User
4 |
5 | # Register your models here.
6 |
7 | from .models import Cart, Product, ProductInCart, Order, ProductInOrder, Deal, Customer, Seller, Contact, SellerAdditional, OtpModel, PremiumProduct
8 |
9 | from django.contrib import admin
10 | from django.contrib.auth.admin import UserAdmin
11 |
12 | from .forms import CustomUserCreationForm, CustomUserChangeForm
13 | from .models import CustomUser
14 |
15 | class SellerAdditionalInline(admin.TabularInline):
16 | model = SellerAdditional
17 |
18 | class CustomUserAdmin(UserAdmin):
19 | add_form = CustomUserCreationForm
20 | form = CustomUserChangeForm
21 | model = CustomUser
22 | list_display = ('email', 'is_staff', 'is_active',)
23 | list_filter = ('email', 'is_staff', 'is_active',)
24 | fieldsets = (
25 | (None, {'fields': ('email', 'phone', 'name','type', 'password')}),
26 | ('Permissions', {'fields': ('is_staff', 'is_active', 'is_superuser', 'groups', 'user_permissions',)}), #'is_customer' , 'is_seller'
27 | )
28 | add_fieldsets = (
29 | (None, {
30 | 'classes': ('wide',),
31 | 'fields': ('email', 'phone', 'name', 'type', 'password1', 'password2', 'is_staff', 'is_active')}
32 | ),
33 | )
34 | search_fields = ('email',)
35 | ordering = ('email',)
36 |
37 | class SellerAdmin(admin.ModelAdmin):
38 | inlines = (
39 | SellerAdditionalInline,
40 | )
41 |
42 |
43 |
44 |
45 |
46 | #admin.site.unregister(User)
47 | admin.site.register(CustomUser, CustomUserAdmin)
48 |
49 |
50 | #admin.site.register(User, UserAdmin)
51 |
52 |
53 | class ProductInCartInline(admin.TabularInline):
54 | model = ProductInCart
55 |
56 | class CartInline(admin.TabularInline):
57 | model = Cart #onetoonefield foreignkey
58 |
59 | class DealInline(admin.TabularInline):
60 | model = Deal.user.through
61 |
62 |
63 | # class UserAdmin(UserAdmin):
64 | # model = User
65 | # list_display = ('username', 'get_cart', 'is_staff', 'is_active',)
66 | # list_filter = ('username', 'is_staff', 'is_active', 'is_superuser')
67 | # fieldsets = (
68 | # (None, {'fields': ('username', 'password')}),
69 | # ('Permissions', {'fields': ('is_staff', ('is_active' , 'is_superuser'), )}),
70 | # ('Important dates',{'fields': ('last_login', 'date_joined')}),
71 | # #('Cart', {'fields': ('get_cart',)})
72 | # ('Advanced options', {
73 | # 'classes': ('collapse',),
74 | # 'fields': ('groups', 'user_permissions'),
75 | # }),
76 | # )
77 | # add_fieldsets = (
78 | # (None, {
79 | # 'classes': ('wide',), # class for css
80 | # 'fields': ('username', 'password1', 'password2', 'is_staff', 'is_active', 'is_superuser', 'groups')} # fields shown on create user page on admin panel
81 | # ),
82 | # )
83 | # inlines = [
84 | # CartInline, DealInline
85 | # ]
86 | # def get_cart(self,obj): # this function only works in list_display
87 | # return obj.cart # through reverse related relationship
88 | # search_fields = ('username',) #search_filter for search bar
89 | # ordering = ('username',)
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 | # from django.utils.html import format_html
100 | # from django.urls import reverse
101 | # def linkify(field_name):
102 | # """
103 | # Converts a foreign key value into clickable links.
104 |
105 | # If field_name is 'parent', link text will be str(obj.parent)
106 | # Link will be admin url for the admin url for obj.parent.id:change
107 | # """
108 | # def _linkify(obj):
109 | # linked_obj = getattr(obj, field_name)
110 | # if linked_obj is None:
111 | # return '-'
112 | # app_label = linked_obj._meta.app_label
113 | # model_name = linked_obj._meta.model_name
114 | # view_name = f'admin:{app_label}_{model_name}_change'
115 | # link_url = reverse(view_name, args=[linked_obj.pk])
116 | # return format_html('{} ', link_url, linked_obj)
117 |
118 | # _linkify.short_description = field_name # Sets column name
119 | # return _linkify
120 |
121 |
122 |
123 | @admin.register(Cart) # through register decorator
124 | class CartAdmin(admin.ModelAdmin):
125 | model = Cart
126 | list_display = ('user','staff', 'created_on',) # here user__is_staff will not work
127 | list_filter = ('user', 'created_on',)
128 | #fields = ('staff',) # either fields or fieldset
129 | fieldsets = (
130 | (None, {'fields': ('user', 'created_on',)}), # only direct relationship no nested relationship('__') ex. user__is_staff
131 | #('User', {'fields': ('staff',)}),
132 | )
133 | inlines = (
134 | ProductInCartInline,
135 | )
136 | # To display only in list_display
137 | def staff(self,obj):
138 | return obj.user.is_staff
139 | # staff.empty_value_display = '???'
140 | staff.admin_order_field = 'user__is_staff' #Allows column order sorting
141 | staff.short_description = 'Staff User' #Renames column head
142 |
143 | #Filtering on side - for some reason, this works
144 | list_filter = ['user__is_staff', 'created_on',] # with direct foreign key(user) no error but not shown in filters, with function error
145 | # ordering = ['user',]
146 | search_fields = ['user__username'] # with direct foreign key no error but filtering not possible directly
147 |
148 |
149 |
150 | # class DealAdmin(admin.ModelAdmin):
151 | # inlines = [
152 | # DealInline,
153 | # ]
154 | # exclude = ('user',)
155 |
156 |
157 |
158 | class ProductInOrderInline(admin.TabularInline):
159 | model = ProductInOrder
160 |
161 | @admin.register(Order) # through register decorator
162 | class CartAdmin(admin.ModelAdmin):
163 | model = Cart
164 | inlines = (
165 | ProductInOrderInline,
166 | )
167 |
168 | #admin.site.register(Cart)
169 | admin.site.register(Product)
170 | admin.site.register(ProductInCart)
171 | admin.site.register(Deal)#, DealAdmin)
172 | #admin.site.register(UserType)
173 | admin.site.register(Customer)
174 | admin.site.register(Seller, SellerAdmin)
175 | admin.site.register(Contact)
176 | admin.site.register(SellerAdditional)
177 |
178 |
179 | #1 using simple database for sessions
180 | from django.contrib.sessions.models import Session
181 | import pprint
182 |
183 | # If you remove this model admin below for sessions then you will see encrypted data only
184 | # class SessionAdmin(admin.ModelAdmin):
185 | # def _session_data(self, obj):
186 | # return obj.get_decoded()
187 | # list_display = ['session_key', '_session_data', 'expire_date']
188 | # admin.site.register(Session, SessionAdmin)
189 |
190 | # class SessionAdmin(admin.ModelAdmin):
191 | # def _session_data(self, obj):
192 | # return pprint.pformat(obj.get_decoded()).replace('\n', ' \n')
193 | # _session_data.allow_tags=True
194 | # list_display = ['session_key', '_session_data', 'expire_date']
195 | # readonly_fields = ['_session_data']
196 | # exclude = ['session_data']
197 | # date_hierarchy='expire_date'
198 |
199 | class SessionAdmin(admin.ModelAdmin):
200 | def _session_data(self, obj):
201 | return pprint.pformat(obj.get_decoded()).replace('\n', ' \n')
202 | _session_data.allow_tags=True
203 | list_display = ['session_key', '_session_data', 'expire_date']
204 | readonly_fields = ['_session_data']
205 | exclude = ['session_data']
206 |
207 | admin.site.register(Session, SessionAdmin)
208 |
209 | admin.site.register(ProductInOrder)
210 |
211 | #admin.site.register(Session)
212 |
213 |
214 | #from django.contrib.sites.models import Site
215 | #admin.site.register(Site)
216 |
217 |
218 | # from django_cache.models import my_cache_table
219 | # admin.site.register(my_cache_table)
220 |
221 |
222 | admin.site.register(OtpModel)
223 | admin.site.register(PremiumProduct)
--------------------------------------------------------------------------------
/firstapp/models.py:
--------------------------------------------------------------------------------
1 | from django.db import models
2 |
3 | from django.contrib.auth.models import User
4 | from django.core.validators import RegexValidator
5 | from django.utils import timezone
6 | # Create your models here.
7 | from multiselectfield import MultiSelectField
8 |
9 |
10 | from django.contrib.auth.models import AbstractUser, AbstractBaseUser
11 | from django.utils.translation import ugettext_lazy as _
12 |
13 | from .managers import CustomUserManager
14 |
15 | from django.contrib.auth.models import PermissionsMixin
16 |
17 | from django.db.models import Q
18 | from datetime import timedelta
19 |
20 |
21 | # class UserType(models.Model):
22 | # CUSTOMER = 1
23 | # SELLER = 2
24 | # TYPE_CHOICES = (
25 | # (SELLER, 'Seller'),
26 | # (CUSTOMER, 'Customer')
27 | # )
28 |
29 | # id = models.PositiveSmallIntegerField(choices=TYPE_CHOICES, primary_key=True)
30 |
31 | # def __str__(self):
32 | # return self.get_id_display()
33 |
34 | class LowercaseEmailField(models.EmailField):
35 | """
36 | Override EmailField to convert emails to lowercase before saving.
37 | """
38 | def to_python(self, value):
39 | """
40 | Convert email to lowercase.
41 | """
42 | value = super(LowercaseEmailField, self).to_python(value)
43 | # Value can be None so check that it's a string before lowercasing.
44 | if isinstance(value, str):
45 | return value.lower()
46 | return value
47 |
48 | class CustomUser(AbstractBaseUser, PermissionsMixin):
49 | # username = None
50 | email = LowercaseEmailField(_('email address'), unique=True)
51 | name = models.CharField(max_length=255)
52 | is_staff = models.BooleanField(default=False)
53 | is_active = models.BooleanField(default=True)
54 | date_joined = models.DateTimeField(default=timezone.now)
55 |
56 | # if you require phone number field in your project
57 | phone_regex = RegexValidator( regex = r'^\d{10}$',message = "phone number should exactly be in 10 digits")
58 | phone = models.CharField(max_length=255, validators=[phone_regex], blank = True, null=True) # you can set it unique = True
59 |
60 | # is_customer = models.BooleanField(default=True)
61 | # is_seller = models.BooleanField(default = False)
62 |
63 | # type = (
64 | # (1, 'Seller'),
65 | # (2, 'Customer')
66 | # )
67 | # user_type = models.IntegerField(choices = type, default=1)
68 |
69 | #usertype = models.ManyToManyField(UserType)
70 |
71 | class Types(models.TextChoices):
72 | SELLER = "Seller", "SELLER"
73 | CUSTOMER = "Customer", "CUSTOMER"
74 |
75 | # Types = (
76 | # (1, 'SELLER'),
77 | # (2, 'CUSTOMER')
78 | # )
79 | # type = models.IntegerField(choices=Types, default=2)
80 |
81 | default_type = Types.CUSTOMER
82 |
83 | #type = models.CharField(_('Type'), max_length=255, choices=Types.choices, default=default_type)
84 | type = MultiSelectField(choices=Types.choices, default=[], null=True, blank=True)
85 |
86 |
87 |
88 | USERNAME_FIELD = 'email'
89 | REQUIRED_FIELDS = []
90 |
91 | objects = CustomUserManager()
92 |
93 | def __str__(self):
94 | return self.email
95 |
96 | #place here
97 | # if not the code below then taking default value in User model not in proxy models
98 | def save(self, *args, **kwargs):
99 | if not self.id:
100 | #self.type = self.default_type
101 | self.type.append(self.default_type)
102 | return super().save(*args, **kwargs)
103 |
104 | class CustomerAdditional(models.Model):
105 | user = models.OneToOneField(CustomUser, on_delete = models.CASCADE)
106 | address = models.CharField(max_length=1000)
107 |
108 |
109 | class SellerAdditional(models.Model):
110 | user = models.OneToOneField(CustomUser, on_delete = models.CASCADE)
111 | gst = models.CharField(max_length=10)
112 | warehouse_location = models.CharField(max_length=1000)
113 |
114 | # Model Managers for proxy models
115 | class SellerManager(models.Manager):
116 | def get_queryset(self, *args, **kwargs):
117 | #return super().get_queryset(*args, **kwargs).filter(type = CustomUser.Types.SELLER)
118 | return super().get_queryset(*args, **kwargs).filter(Q(type__contains = CustomUser.Types.SELLER))
119 |
120 | class CustomerManager(models.Manager):
121 | def get_queryset(self, *args, **kwargs):
122 | #return super().get_queryset(*args, **kwargs).filter(type = CustomUser.Types.CUSTOMER)
123 | return super().get_queryset(*args, **kwargs).filter(Q(type__contains = CustomUser.Types.CUSTOMER))
124 |
125 |
126 | # Proxy Models. They do not create a seperate table
127 | class Seller(CustomUser):
128 | default_type = CustomUser.Types.SELLER
129 | objects = SellerManager()
130 | class Meta:
131 | proxy = True
132 |
133 | def sell(self):
134 | print("I can sell")
135 |
136 | @property
137 | def showAdditional(self):
138 | return self.selleradditional
139 |
140 | class Customer(CustomUser):
141 | default_type = CustomUser.Types.CUSTOMER
142 | objects = CustomerManager()
143 | class Meta:
144 | proxy = True
145 |
146 | def buy(self):
147 | print("I can buy")
148 |
149 | @property
150 | def showAdditional(self):
151 | return self.customeradditional
152 |
153 |
154 | class Contact(models.Model):
155 | email = models.EmailField()
156 | name = models.CharField(max_length=5)
157 | phone_regex = RegexValidator( regex = r'^\d{10}$',message = "phone number should exactly be in 10 digits")
158 | phone = models.CharField(max_length=255, validators=[phone_regex])
159 | query = models.TextField()
160 |
161 |
162 |
163 |
164 |
165 | class Product(models.Model):
166 | product_id = models.AutoField(primary_key=True)
167 | product_name = models.CharField(max_length=15)
168 | image = models.ImageField(upload_to = "firstapp/productimages", default = None, null = True, blank = True)
169 | price = models.FloatField()
170 | brand = models.CharField(max_length=1000)
171 | date_added = models.DateTimeField(default=timezone.now)
172 |
173 | #class Meta:
174 | #ordering = ['-price'] # default ordering whenever you query to database retrieval in order as stored in DB ---> ordering ---> returned as a queryset where called
175 |
176 | @classmethod
177 | def updateprice(cls,product_id, price):
178 | product = cls.objects.filter(product_id = product_id)
179 | product = product.first()
180 | product.price = price
181 | product.save()
182 | return product
183 |
184 | @classmethod
185 | def create(cls, product_name, price):
186 | product = Product(product_name = product_name, price = price)
187 | product.save()
188 | return product
189 |
190 |
191 | # @staticmethod
192 | # def a_static_method():
193 | # """A static method has no information about instances or classes
194 | # unless explicitly given. It just lives in the class (and thus its
195 | # instances') namespace.
196 | # """
197 | # return some_function_h()
198 |
199 | def __str__(self):
200 | return self.product_name
201 |
202 |
203 |
204 | class CartManager(models.Manager):
205 | def create_cart(self, user):
206 | cart = self.create(user = user)
207 | # you can perform more operations
208 | return cart
209 |
210 | class Cart(models.Model):
211 | cart_id = models.AutoField(primary_key=True)
212 | user = models.OneToOneField(CustomUser, on_delete = models.CASCADE)
213 | created_on = models.DateTimeField(default=timezone.now)
214 |
215 | objects = CartManager()
216 |
217 | class ProductInCart(models.Model):
218 | class Meta:
219 | unique_together = (('cart', 'product'),)
220 | product_in_cart_id = models.AutoField(primary_key=True)
221 | cart = models.ForeignKey(Cart, on_delete = models.CASCADE)
222 | product = models.ForeignKey(Product, on_delete = models.CASCADE)
223 | quantity = models.PositiveIntegerField()
224 |
225 |
226 | class Order(models.Model):
227 | status_choices = (
228 | (1, 'Not Packed'),
229 | (2, 'Ready For Shipment'),
230 | (3, 'Shipped'),
231 | (4, 'Delivered')
232 | )
233 | payment_status_choices = (
234 | (1, 'SUCCESS'),
235 | (2, 'FAILURE' ),
236 | (3, 'PENDING'),
237 | )
238 | user = models.ForeignKey(CustomUser, on_delete=models.CASCADE)
239 | status = models.IntegerField(choices = status_choices, default=1)
240 |
241 | total_amount = models.FloatField()
242 | payment_status = models.IntegerField(choices = payment_status_choices, default=3)
243 | order_id = models.CharField(unique=True, max_length=100, null=True, blank=True, default=None)
244 | datetime_of_payment = models.DateTimeField(default=timezone.now)
245 | # related to razorpay
246 | razorpay_order_id = models.CharField(max_length=500, null=True, blank=True)
247 | razorpay_payment_id = models.CharField(max_length=500, null=True, blank=True)
248 | razorpay_signature = models.CharField(max_length=500, null=True, blank=True)
249 |
250 |
251 | def save(self, *args, **kwargs):
252 | if self.order_id is None and self.datetime_of_payment and self.id:
253 | self.order_id = self.datetime_of_payment.strftime('PAY2ME%Y%m%dODR') + str(self.id)
254 | return super().save(*args, **kwargs)
255 |
256 | def __str__(self):
257 | return self.user.email + " " + str(self.id)
258 |
259 |
260 | class ProductInOrder(models.Model):
261 | class Meta:
262 | unique_together = (('order', 'product'),)
263 | order = models.ForeignKey(Order, on_delete = models.CASCADE)
264 | product = models.ForeignKey(Product, on_delete = models.CASCADE)
265 | quantity = models.PositiveIntegerField()
266 | price = models.FloatField()
267 |
268 |
269 |
270 |
271 | class Deal(models.Model):
272 | user = models.ManyToManyField(CustomUser)
273 | deal_name = models.CharField(max_length=255)
274 |
275 |
276 |
277 | class OtpModel(models.Model):
278 | otp_regex = RegexValidator( regex = r'^\d{6}$',message = "otp should be in six digits")
279 | otp = models.CharField(max_length=6, validators=[otp_regex])
280 | phone_regex = RegexValidator( regex = r'^\d{10}$',message = "phone number should exactly be in 10 digits")
281 | phone = models.CharField(max_length=255, validators=[phone_regex])
282 | expiry = models.DateTimeField(default = (timezone.now() + timedelta(minutes = 5)))
283 | is_verified = models.BooleanField(default=False)
284 |
285 | # expiry_after_verified
286 |
287 |
288 |
289 | # class Category(models.Model):
290 | # category_name = models.CharField(max_length=1000)
291 |
292 | # class Subcategory(models.Model):
293 | # subcategory_name = models.CharField(max_length=1000)
294 | # category = models.ForeignKey(Category, on_delete = models.CASCADE)
295 |
296 |
297 |
298 | class PremiumProduct(models.Model):
299 | product_name = models.CharField(max_length=15)
300 | image = models.ImageField(upload_to = "firstapp/premiumproductimages", default = None, null = True, blank = True)
301 | price = models.FloatField()
302 | brand = models.CharField(max_length=1000)
303 | date_added = models.DateTimeField(default=timezone.now)
304 |
305 | # # custom permissions dependent to a specific model
306 | class Meta:
307 | permissions = (
308 | ('can_avail_premium_delivery', 'can avail for premium delivery on premium products'),
309 | ('can_add_premium_discount', 'can avail more premium discount on premium products')
310 | )
311 |
312 |
313 |
314 | class CustomPermissions(models.Model):
315 |
316 | class Meta:
317 |
318 | managed = False # No database table creation or deletion \
319 | # operations will be performed for this model.
320 |
321 | default_permissions = () # disable "add", "change", "delete"
322 | # and "view" default permissions
323 |
324 | # All the custom permissions not related to models on Manufacturer
325 | permissions = (
326 | ('accept_order', 'can accept order'),
327 | ('reject_order', 'can reject order'),
328 | ('view_order', 'can view order'),
329 | ('change_order', 'can change order'),
330 | ('view_return', 'can view return'),
331 | ('accept_return', 'can accept return'),
332 | ('reject_return', 'can reject return'),
333 | ('change_return', 'can change return'),
334 | ('view_dashboard', 'can view dashboard'),
335 | )
336 |
337 |
338 |
339 |
340 |
341 |
342 |
343 |
344 |
345 |
346 |
347 |
348 |
349 |
350 |
351 |
352 |
353 |
354 |
355 |
356 |
357 |
358 |
359 |
360 |
361 |
362 |
363 |
364 |
365 |
366 |
367 |
368 |
369 |
370 |
371 |
372 |
--------------------------------------------------------------------------------
/seller/templates/seller/index.html:
--------------------------------------------------------------------------------
1 | {% extends 'seller/basic.html' %}
2 | {% load static %}
3 | {% block title %}
4 | {% endblock %}
5 | {% block css %}
6 |
121 | {% endblock %}
122 |
123 | {% block body%}
124 |
125 |
126 | Hello World!
127 |
128 | {{age}}
129 | {{array}}
130 | {% for i in array %}
131 | {{i}}
132 | {% endfor %}
133 | {{dic}}
134 | {% for key,value in dic.items %}
135 | {{key}} {{value}}
136 | {% endfor %}
137 |
138 | {{dic.a}}
139 |
140 |
141 |
142 |
143 |
144 |
145 |
146 |
147 |
149 |
150 |
151 |
152 |
153 |
Example headline.
154 |
Cras justo odio, dapibus ac facilisis in, egestas eget quam. Donec id elit non mi porta
155 | gravida at eget metus. Nullam id dolor id nibh ultricies vehicula ut id elit.
156 |
Sign up today
157 |
158 |
159 |
160 |
161 |
163 |
164 |
165 |
166 |
167 |
Another example headline.
168 |
Cras justo odio, dapibus ac facilisis in, egestas eget quam. Donec id elit non mi porta
169 | gravida at eget metus. Nullam id dolor id nibh ultricies vehicula ut id elit.
170 |
Learn more
171 |
172 |
173 |
174 |
175 |
177 |
178 |
179 |
180 |
181 |
One more for good measure.
182 |
Cras justo odio, dapibus ac facilisis in, egestas eget quam. Donec id elit non mi porta
183 | gravida at eget metus. Nullam id dolor id nibh ultricies vehicula ut id elit.
184 |
Browse gallery
185 |
186 |
187 |
188 |
189 |
190 |
191 | Previous
192 |
193 |
194 |
195 | Next
196 |
197 |
198 |
199 |
200 |
202 |
203 |
204 |
205 |
206 |
207 |
208 |
209 |
212 | Placeholder
213 | 140x140
215 |
216 |
217 |
Heading
218 |
Donec sed odio dui. Etiam porta sem malesuada magna mollis euismod. Nullam id dolor id nibh ultricies
219 | vehicula ut id elit. Morbi leo risus, porta ac consectetur ac, vestibulum at eros. Praesent commodo
220 | cursus magna.
221 |
View details »
222 |
223 |
224 |
227 | Placeholder
228 | 140x140
230 |
231 |
232 |
Heading
233 |
Duis mollis, est non commodo luctus, nisi erat porttitor ligula, eget lacinia odio sem nec elit. Cras
234 | mattis consectetur purus sit amet fermentum. Fusce dapibus, tellus ac cursus commodo, tortor mauris
235 | condimentum nibh.
236 |
View details »
237 |
238 |
239 |
242 | Placeholder
243 | 140x140
245 |
246 |
247 |
Heading
248 |
Donec sed odio dui. Cras justo odio, dapibus ac facilisis in, egestas eget quam. Vestibulum id ligula
249 | porta felis euismod semper. Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh,
250 | ut fermentum massa justo sit amet risus.
251 |
View details »
252 |
253 |
254 |
255 |
256 |
257 |
258 |
259 |
260 |
261 |
262 |
First featurette heading. It’ll blow your
263 | mind.
264 |
Donec ullamcorper nulla non metus auctor fringilla. Vestibulum id ligula porta felis
265 | euismod semper. Praesent commodo cursus magna, vel scelerisque nisl consectetur. Fusce dapibus,
266 | tellus ac cursus commodo.
267 |
268 |
269 |
272 | Placeholder
273 | 500x500
275 |
276 |
277 |
278 |
279 |
280 |
281 |
282 |
283 |
284 |
Oh yeah, it’s that good. See for
285 | yourself.
286 |
Donec ullamcorper nulla non metus auctor fringilla. Vestibulum id ligula porta felis
287 | euismod semper. Praesent commodo cursus magna, vel scelerisque nisl consectetur. Fusce dapibus,
288 | tellus ac cursus commodo.
289 |
290 |
291 |
294 | Placeholder
295 | 500x500
297 |
298 |
299 |
300 |
301 |
302 |
303 |
304 |
305 |
306 |
And lastly, this one. Checkmate.
307 |
Donec ullamcorper nulla non metus auctor fringilla. Vestibulum id ligula porta felis
308 | euismod semper. Praesent commodo cursus magna, vel scelerisque nisl consectetur. Fusce dapibus,
309 | tellus ac cursus commodo.
310 |
311 |
312 |
315 | Placeholder
316 | 500x500
318 |
319 |
320 |
321 |
322 |
323 |
324 |
325 |
326 |
327 |
328 |
329 |
330 |
331 |
335 |
336 |
337 |
338 |
341 |
342 | {% endblock %}
--------------------------------------------------------------------------------
/firstapp/templates/firstapp/index.html:
--------------------------------------------------------------------------------
1 | {% extends 'firstapp/basic.html' %}
2 | {% load static %}
3 | {% block title %}
4 | {% endblock %}
5 | {% block css %}
6 |
121 | {% endblock %}
122 |
123 | {% block body%}
124 |
125 |
126 | Hello World!
127 |
128 | {{request.session.test}}
129 | {% if user.is_authenticated %}
130 | {{user.name}}
131 | {% endif %}
132 | {{age}}
133 | {{array}}
134 | {% for i in array %}
135 | {{i}}
136 | {% endfor %}
137 | {{dic}}
138 | {% for key,value in dic.items %}
139 | {{key}} {{value}}
140 | {% endfor %}
141 |
142 | {{dic.a}}
143 |
144 |
145 |
146 |
147 |
148 |
149 |
150 |
151 |
153 |
154 |
155 |
156 |
157 |
Example headline.
158 |
Cras justo odio, dapibus ac facilisis in, egestas eget quam. Donec id elit non mi porta
159 | gravida at eget metus. Nullam id dolor id nibh ultricies vehicula ut id elit.
160 |
Sign up today
161 |
162 |
163 |
164 |
165 |
167 |
168 |
169 |
170 |
171 |
Another example headline.
172 |
Cras justo odio, dapibus ac facilisis in, egestas eget quam. Donec id elit non mi porta
173 | gravida at eget metus. Nullam id dolor id nibh ultricies vehicula ut id elit.
174 |
Learn more
175 |
176 |
177 |
178 |
179 |
181 |
182 |
183 |
184 |
185 |
One more for good measure.
186 |
Cras justo odio, dapibus ac facilisis in, egestas eget quam. Donec id elit non mi porta
187 | gravida at eget metus. Nullam id dolor id nibh ultricies vehicula ut id elit.
188 |
Browse gallery
189 |
190 |
191 |
192 |
193 |
194 |
195 | Previous
196 |
197 |
198 |
199 | Next
200 |
201 |
202 |
203 |
204 |
206 |
207 |
208 |
209 |
210 |
211 |
212 |
213 |
216 | Placeholder
217 | 140x140
219 |
220 |
221 |
Heading
222 |
Donec sed odio dui. Etiam porta sem malesuada magna mollis euismod. Nullam id dolor id nibh ultricies
223 | vehicula ut id elit. Morbi leo risus, porta ac consectetur ac, vestibulum at eros. Praesent commodo
224 | cursus magna.
225 |
View details »
226 |
227 |
228 |
231 | Placeholder
232 | 140x140
234 |
235 |
236 |
Heading
237 |
Duis mollis, est non commodo luctus, nisi erat porttitor ligula, eget lacinia odio sem nec elit. Cras
238 | mattis consectetur purus sit amet fermentum. Fusce dapibus, tellus ac cursus commodo, tortor mauris
239 | condimentum nibh.
240 |
View details »
241 |
242 |
243 |
246 | Placeholder
247 | 140x140
249 |
250 |
251 |
Heading
252 |
Donec sed odio dui. Cras justo odio, dapibus ac facilisis in, egestas eget quam. Vestibulum id ligula
253 | porta felis euismod semper. Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh,
254 | ut fermentum massa justo sit amet risus.
255 |
View details »
256 |
257 |
258 |
259 |
260 |
261 |
262 |
263 |
264 |
265 |
266 |
First featurette heading. It’ll blow your
267 | mind.
268 |
Donec ullamcorper nulla non metus auctor fringilla. Vestibulum id ligula porta felis
269 | euismod semper. Praesent commodo cursus magna, vel scelerisque nisl consectetur. Fusce dapibus,
270 | tellus ac cursus commodo.
271 |
272 |
273 |
276 | Placeholder
277 | 500x500
279 |
280 |
281 |
282 |
283 |
284 |
285 |
286 |
287 |
288 |
Oh yeah, it’s that good. See for
289 | yourself.
290 |
Donec ullamcorper nulla non metus auctor fringilla. Vestibulum id ligula porta felis
291 | euismod semper. Praesent commodo cursus magna, vel scelerisque nisl consectetur. Fusce dapibus,
292 | tellus ac cursus commodo.
293 |
294 |
295 |
298 | Placeholder
299 | 500x500
301 |
302 |
303 |
304 |
305 |
306 |
307 |
308 |
309 |
310 |
And lastly, this one. Checkmate.
311 |
Donec ullamcorper nulla non metus auctor fringilla. Vestibulum id ligula porta felis
312 | euismod semper. Praesent commodo cursus magna, vel scelerisque nisl consectetur. Fusce dapibus,
313 | tellus ac cursus commodo.
314 |
315 |
316 |
319 | Placeholder
320 | 500x500
322 |
323 |
324 |
325 |
326 |
327 |
328 |
329 |
330 |
331 |
332 |
333 |
334 |
335 |
339 |
340 |
341 |
342 |
345 |
346 | {% endblock %}
--------------------------------------------------------------------------------
/firstapp/views.py:
--------------------------------------------------------------------------------
1 | from django.shortcuts import render, HttpResponse, redirect
2 | from django.http import JsonResponse
3 | from django.views.generic import TemplateView, FormView, CreateView, ListView, UpdateView, DeleteView, DetailView, View
4 | from django.core.exceptions import ValidationError
5 | from .forms import ContactUsForm, RegistrationFormSeller, RegistrationForm, RegistrationFormSeller2, CartForm
6 | from django.urls import reverse_lazy, reverse
7 | from .models import SellerAdditional, CustomUser, Contact, Product, ProductInCart, Cart
8 | from django.contrib.auth.views import LoginView, LogoutView
9 | from django.contrib.auth.mixins import LoginRequiredMixin
10 |
11 |
12 | from firstproject import settings
13 | from django.core.mail import EmailMessage
14 | from .tokens import account_activation_token
15 | from django.core.mail import send_mail
16 | from django.contrib.sites.shortcuts import get_current_site
17 | from django.utils.encoding import force_bytes, force_text
18 | from django.utils.http import urlsafe_base64_encode, urlsafe_base64_decode
19 | from django.template.loader import render_to_string
20 | from django.contrib.auth import authenticate, login, logout
21 | from django.contrib import messages
22 |
23 | from django.contrib.auth.decorators import login_required
24 | from django.views.decorators.csrf import csrf_exempt
25 |
26 |
27 | # Create your views here.
28 | # def index(request):
29 | # age = 10
30 | # arr = ['priyanshu', 'aman', 'rohit', 'shubhi']
31 | # dic = {'a':'one', 'b':'two'}
32 | # return render(request, 'firstapp/index.html', {'age':age, 'array':arr, 'dic':dic})
33 | # #return HttpResponse("Hello ")
34 |
35 | class Index(TemplateView):
36 | template_name = 'firstapp/index.html'
37 | def get_context_data(self, **kwargs):
38 | age = 10
39 | arr = ['priyanshu', 'aman', 'rohit', 'shubhi']
40 | dic = {'a':'one', 'b':'two'}
41 | context_old = super().get_context_data(**kwargs)
42 | context = {'age':age, 'array':arr, 'dic':dic, 'context_old':context_old}
43 | return context
44 |
45 |
46 |
47 | def testsessions(request):
48 | if request.session.get('test', False):
49 | print(request.session["test"])
50 | #request.session.set_expiry(1)
51 | # if request.session['test']:
52 | # print(request.session['test'])
53 | request.session['test'] = "testing"
54 | request.session['test2'] = "testing2"
55 | return render(request, "firstapp/sessiontesting.html")
56 |
57 |
58 |
59 |
60 |
61 | def contactus(request):
62 | if request.method == 'POST':
63 | name = request.POST.get('name')
64 | email = request.POST.get('email')
65 | phone = request.POST['phone']
66 | if len(phone)<10 or len(phone)>10:
67 | raise ValidationError("Phone number length is not right")
68 | query = request.POST['query']
69 | print(name + " " + email + " " + phone + " " +query)
70 | return render(request, 'firstapp/contactus.html')
71 |
72 | def contactus2(request):
73 | if request.method == 'POST':
74 | form = ContactUsForm(request.POST)
75 | if form.is_valid(): #clean_data
76 | if len(form.cleaned_data.get('query'))>10:
77 | form.add_error('query', 'Query length is not right')
78 | return render(request, 'firstapp/contactus2.html', {'form':form})
79 | form.save()
80 | return HttpResponse("Thank YOu")
81 | else:
82 | if len(form.cleaned_data.get('query'))>10:
83 | #form.add_error('query', 'Query length is not right')
84 | form.errors['__all__'] = 'Query length is not right. It should be in 10 digits.'
85 | return render(request, 'firstapp/contactus2.html', {'form':form})
86 | return render(request, 'firstapp/contactus2.html', {'form':ContactUsForm})
87 |
88 | class ContactUs(FormView):
89 | form_class = ContactUsForm
90 | template_name = 'firstapp/contactus2.html'
91 | #success_url = '/' #hardcoded url
92 | success_url = reverse_lazy('index')
93 | def form_valid(self, form):
94 | if len(form.cleaned_data.get('query'))>10:
95 | form.add_error('query', 'Query length is not right')
96 | return render(self.request, 'firstapp/contactus2.html', {'form':form})
97 | form.save()
98 | response = super().form_valid(form)
99 | return response
100 |
101 | def form_invalid(self, form):
102 | if len(form.cleaned_data.get('query'))>10:
103 | form.add_error('query', 'Query length is not right')
104 | #form.errors['__all__'] = 'Query length is not right. It should be in 10 digits.'
105 | response = super().form_invalid(form)
106 | return response
107 |
108 |
109 | # class RegisterViewSeller(CreateView):
110 | # template_name = 'firstapp/register.html'
111 | # form_class = RegistrationFormSeller
112 | # success_url = reverse_lazy('index')
113 |
114 | # def post(self, request, *args, **kwargs):
115 | # response = super().post(request, *args, **kwargs)
116 | # if response.status_code == 302:
117 | # gst = request.POST.get('gst')
118 | # warehouse_location = request.POST.get('warehouse_location')
119 | # user = CustomUser.objects.get(email = request.POST.get('email'))
120 | # s_add = SellerAdditional.objects.create(user = user, gst = gst, warehouse_location = warehouse_location)
121 | # return response
122 | # else:
123 | # return response
124 |
125 | class RegisterView(CreateView):
126 | template_name = 'firstapp/registerbasicuser.html'
127 | form_class = RegistrationForm
128 | success_url = reverse_lazy('signup')
129 |
130 | def post(self, request, *args, **kwargs):
131 | #form = RegistrationForm(request.POST)
132 | user_email = request.POST.get('email')
133 | try:
134 | existing_user = CustomUser.objects.get(email = user_email)
135 | if(existing_user.is_active == False):
136 | existing_user.delete()
137 | except:
138 | pass
139 | response = super().post(request, *args, **kwargs)
140 | if response.status_code == 302:
141 | user = CustomUser.objects.get(email = user_email)
142 | user.is_active = False
143 | user.save()
144 | current_site = get_current_site(request) #www.wondershop.in:8000 127.0.0.1:8000
145 | mail_subject = 'Activate your account.'
146 | message = render_to_string('firstapp/registration/acc_active_email.html', {
147 | 'user': user,
148 | 'domain': current_site.domain,
149 | 'uid':urlsafe_base64_encode(force_bytes(user.pk)),
150 | 'token':account_activation_token.make_token(user),
151 | })
152 | #print(message)
153 | to_email = user_email
154 | #form = RegistrationForm(request.POST) # here we are again calling all its validations
155 | form = self.get_form()
156 | try:
157 | send_mail(
158 | subject=mail_subject,
159 | message=message,
160 | from_email=settings.EMAIL_HOST_USER,
161 | recipient_list= [to_email],
162 | fail_silently=False, # if it fails due to some error or email id then it get silenced without affecting others
163 | )
164 | messages.success(request, "link has been sent to your email id. please check your inbox and if its not there check your spam as well.")
165 | return self.render_to_response({'form':form})
166 | except:
167 | form.add_error('', 'Error Occured In Sending Mail, Try Again')
168 | messages.error(request, "Error Occured In Sending Mail, Try Again")
169 | return self.render_to_response({'form':form})
170 | else:
171 | return response
172 |
173 |
174 | def activate(request, uidb64, token):
175 | try:
176 | uid = force_text(urlsafe_base64_decode(uidb64))
177 | user = CustomUser.objects.get(pk=uid)
178 | except(TypeError, ValueError, OverflowError, CustomUser.DoesNotExist) as e:
179 | user = None
180 | if user is not None and account_activation_token.check_token(user, token):
181 | user.is_active = True
182 | user.save()
183 | login(request, user)
184 | messages.success(request, "Successfully Logged In")
185 | return redirect(reverse_lazy('index'))
186 | # return HttpResponse('Thank you for your email confirmation. Now you can login your account.')
187 | else:
188 | return HttpResponse('Activation link is invalid or your account is already Verified! Try To Login')
189 |
190 | class LoginViewUser(LoginView):
191 | template_name = "firstapp/login.html"
192 | #success_url = reverse_lazy('index')
193 |
194 | # class RegisterViewSeller(LoginRequiredMixin, CreateView):
195 | # template_name = 'firstapp/registerseller.html'
196 | # form_class = RegistrationFormSeller2
197 | # success_url = reverse_lazy('index')
198 |
199 | # def form_valid(self, form):
200 | # user = self.request.user
201 | # user.type.append(user.Types.SELLER)
202 | # user.save()
203 | # form.instance.user = self.request.user
204 | # return super().form_valid(form)
205 |
206 |
207 | class LogoutViewUser(LogoutView):
208 | success_url = reverse_lazy('index')
209 |
210 |
211 | class ListProducts(ListView):
212 | template_name = "firstapp/listproducts.html"
213 | model = Product
214 | context_object_name = "product"
215 | paginate_by = 2
216 |
217 |
218 |
219 | from django.core.paginator import EmptyPage, PageNotAnInteger, Paginator
220 | import json
221 | from django.core.serializers.json import DjangoJSONEncoder
222 | from django.db.models import Q
223 | PRODUCTS_PER_PAGE = 2
224 | def listProducts(request):
225 |
226 | ordering = request.GET.get('ordering', "") # http://www.wondershop.in:8000/listproducts/?page=1&ordering=price
227 | search = request.GET.get('search', "")
228 | price = request.GET.get('price', "")
229 |
230 | if search:
231 | product = Product.objects.filter(Q(product_name__icontains=search) | Q(brand__icontains=search)) # SQLite doesn’t support case-sensitive LIKE statements; contains acts like icontains for SQLite
232 |
233 | else:
234 | product = Product.objects.all()
235 |
236 | if ordering:
237 | product = product.order_by(ordering)
238 |
239 | if price:
240 | product = product.filter(price__lt = price)
241 |
242 |
243 | # Pagination
244 | page = request.GET.get('page',1)
245 | product_paginator = Paginator(product, PRODUCTS_PER_PAGE)
246 | try:
247 | product = product_paginator.page(page)
248 | except EmptyPage:
249 | product = product_paginator.page(product_paginator.num_pages)
250 | except:
251 | product = product_paginator.page(PRODUCTS_PER_PAGE)
252 | return render(request, "firstapp/listproducts.html", {"product":product, 'page_obj':product, 'is_paginated':True, 'paginator':product_paginator})
253 |
254 |
255 | def suggestionApi(request):
256 | if 'term' in request.GET:
257 | search = request.GET.get('term')
258 | qs = Product.objects.filter(Q(product_name__icontains=search))[0:10]
259 | # print(list(qs.values()))
260 | # print(json.dumps(list(qs.values()), cls = DjangoJSONEncoder))
261 | titles = list()
262 | for product in qs:
263 | titles.append(product.product_name)
264 | #print(titles)
265 | if len(qs)<10:
266 | length = 10 - len(qs)
267 | qs2 = Product.objects.filter(Q(brand__icontains=search))[0:length]
268 | for product in qs2:
269 | titles.append(product.brand)
270 | return JsonResponse(titles, safe=False) # [1,2,3,4] ---> "[1,2,3,4]" queryset ---> serialize into list or dict format ---> json format using json.dumps with a DjangoJSONEncoder(encoder to handle datetime like objects)
271 |
272 |
273 |
274 |
275 | def listProductsApi(request):
276 | # print(Product.objects.all())
277 | # print(Product.objects.values())
278 | #result = json.dumps(list(Product.objects.values()), sort_keys=False, indent=0, cls=DjangoJSONEncoder) # will return error if you have a datetime object as it is not jsonserializable so thats why use DjangoJSONEncoder, indent to beautify and sort_keys to sort keys
279 | #print(type(result)) #str type
280 | #print(result)
281 | result = list(Product.objects.values()) # will work like passing queryset as a context data if used by a template
282 | #print(result)
283 | #return render(request, "firstapp/listproducts.html", {"product":result})
284 | return JsonResponse(result, safe=False)
285 |
286 |
287 |
288 |
289 | class ProductDetail(DetailView):
290 | model = Product
291 | template_name = "firstapp/productdetail.html"
292 | context_object_name = "product"
293 |
294 |
295 | @login_required
296 | def addToCart(request, id):
297 | try:
298 | cart = Cart.objects.get(user = request.user)
299 | try:
300 | product = Product.objects.get(product_id = id)
301 | try:
302 | productincart = ProductInCart.objects.get(cart = cart, product = product)
303 | productincart.quantity = productincart.quantity + 1
304 | productincart.save()
305 | messages.success(request, "Successfully added to cart")
306 | return redirect(reverse_lazy("displaycart"))
307 | except:
308 | productincart = ProductInCart.objects.create(cart = cart, product = product, quantity=1)
309 | messages.success(request, "Successfully added to cart")
310 | return redirect(reverse_lazy("displaycart"))
311 | except:
312 | messages.error(request, "Product can not be found")
313 | return redirect(reverse_lazy('listproducts'))
314 | except:
315 | cart = Cart.objects.create(user = request.user)
316 | try:
317 | product = Product.objects.get(product_id = id)
318 | productincart = ProductInCart.objects.create(cart = cart, product = product, quantity = 1)
319 | messages.success(request, "Successfully added to cart")
320 | return redirect(reverse_lazy("displaycart"))
321 | except:
322 | messages.error(request, "Error in adding to cart. Please try again")
323 | return redirect(reverse_lazy('listproducts'))
324 |
325 |
326 | class DisplayCart(LoginRequiredMixin, ListView):
327 | model = ProductInCart
328 | template_name = "firstapp/displaycart.html"
329 | context_object_name = "cart"
330 |
331 | def get_queryset(self):
332 | queryset = ProductInCart.objects.filter(cart = self.request.user.cart)
333 | return queryset
334 |
335 | class UpdateCart(LoginRequiredMixin, UpdateView):
336 | model = ProductInCart
337 | form_class = CartForm
338 | success_url = reverse_lazy("displaycart")
339 |
340 | def post(self, request, *args, **kwargs):
341 | response = super().post(request, *args, **kwargs)
342 | if response.status_code == 302:
343 | if int(request.POST.get("quantity")) == 0:
344 | productincart = self.get_object()
345 | productincart.delete()
346 | return response
347 | else:
348 | messages.error(request, "error in quantity")
349 | return redirect(reverse_lazy("displaycart"))
350 |
351 | class DeleteFromCart(LoginRequiredMixin, DeleteView):
352 | model = ProductInCart
353 | success_url = reverse_lazy("displaycart")
354 |
355 |
356 |
357 | # Adding Payment Gateway
358 | import razorpay
359 | razorpay_client = razorpay.Client(auth=(settings.razorpay_id, settings.razorpay_account_id))
360 |
361 | from .models import Order, ProductInOrder, Cart
362 |
363 | @login_required
364 | def payment(request):
365 | if request.method == "POST":
366 | try:
367 | cart = Cart.objects.get(user = request.user)
368 | products_in_cart = ProductInCart.objects.filter(cart = cart)
369 | final_price = 0
370 | if(len(products_in_cart)>0):
371 | order = Order.objects.create(user = request.user, total_amount = 0)
372 | # order.save()
373 | for product in products_in_cart:
374 | product_in_order = ProductInOrder.objects.create(order = order, product = product.product, quantity = product.quantity, price = product.product.price)
375 | final_price = final_price + (product.product.price * product.quantity)
376 | else:
377 | return HttpResponse("No product in cart")
378 | except:
379 | return HttpResponse("No product in cart")
380 |
381 | order.total_amount = final_price
382 | order.save()
383 |
384 | order_currency = 'INR'
385 |
386 | callback_url = 'http://'+ str(get_current_site(request))+"/handlerequest/"
387 | print(callback_url)
388 | notes = {'order-type': "basic order from the website", 'key':'value'}
389 | razorpay_order = razorpay_client.order.create(dict(amount=final_price*100, currency=order_currency, notes = notes, receipt=order.order_id, payment_capture='0'))
390 | print(razorpay_order['id'])
391 | order.razorpay_order_id = razorpay_order['id']
392 | order.save()
393 |
394 | return render(request, 'firstapp/payment/paymentsummaryrazorpay.html', {'order':order, 'order_id': razorpay_order['id'], 'orderId':order.order_id, 'final_price':final_price, 'razorpay_merchant_id':settings.razorpay_id, 'callback_url':callback_url})
395 | else:
396 | return HttpResponse("505 Not Found")
397 |
398 |
399 | # for generating pdf invoice
400 | from io import BytesIO
401 | from django.http import HttpResponse
402 | from django.template.loader import get_template
403 | from xhtml2pdf import pisa
404 | import os
405 |
406 |
407 |
408 | def fetch_resources(uri, rel):
409 | path = os.path.join(uri.replace(settings.STATIC_URL, ""))
410 | return path
411 |
412 | def render_to_pdf(template_src, context_dict={}):
413 | template = get_template(template_src)
414 | html = template.render(context_dict)
415 | result = BytesIO()
416 | pdf = pisa.pisaDocument(BytesIO(html.encode("ISO-8859-1")), result)#, link_callback=fetch_resources)
417 | if not pdf.err:
418 | return HttpResponse(result.getvalue(), content_type='application/pdf')
419 | return None
420 |
421 | from django.core.mail import EmailMultiAlternatives
422 | @csrf_exempt
423 | def handlerequest(request):
424 | if request.method == "POST":
425 | try:
426 | payment_id = request.POST.get('razorpay_payment_id', '')
427 | order_id = request.POST.get('razorpay_order_id','')
428 | signature = request.POST.get('razorpay_signature','')
429 | params_dict = {
430 | 'razorpay_order_id': order_id,
431 | 'razorpay_payment_id': payment_id,
432 | 'razorpay_signature': signature
433 | }
434 | try:
435 | order_db = Order.objects.get(razorpay_order_id=order_id)
436 | except:
437 | return HttpResponse("505 Not Found")
438 | order_db.razorpay_payment_id = payment_id
439 | order_db.razorpay_signature = signature
440 | order_db.save()
441 | result = razorpay_client.utility.verify_payment_signature(params_dict)
442 | if result==None:
443 | amount = order_db.total_amount * 100 #we have to pass in paisa
444 | try:
445 | razorpay_client.payment.capture(payment_id, amount)
446 | order_db.payment_status = 1
447 | order_db.save()
448 |
449 | ## For generating Invoice PDF
450 | template = get_template('firstapp/payment/invoice.html')
451 | data = {
452 | 'order_id': order_db.order_id,
453 | 'transaction_id': order_db.razorpay_payment_id,
454 | 'user_email': order_db.user.email,
455 | 'date': str(order_db.datetime_of_payment),
456 | 'name': order_db.user.name,
457 | 'order': order_db,
458 | 'amount': order_db.total_amount,
459 | }
460 | html = template.render(data)
461 | result = BytesIO()
462 | pdf = pisa.pisaDocument(BytesIO(html.encode("ISO-8859-1")), result)#, link_callback=fetch_resources)
463 | pdf = result.getvalue()
464 | filename = 'Invoice_' + data['order_id'] + '.pdf'
465 |
466 | mail_subject = 'Recent Order Details'
467 | # message = render_to_string('firstapp/payment/emailinvoice.html', {
468 | # 'user': order_db.user,
469 | # 'order': order_db
470 | # })
471 | context_dict = {
472 | 'user': order_db.user,
473 | 'order': order_db
474 | }
475 | template = get_template('firstapp/payment/emailinvoice.html')
476 | message = template.render(context_dict)
477 | to_email = order_db.user.email
478 | # email = EmailMessage(
479 | # mail_subject,
480 | # message,
481 | # settings.EMAIL_HOST_USER,
482 | # [to_email]
483 | # )
484 |
485 | # for including css(only inline css works) in mail and remove autoescape off
486 | email = EmailMultiAlternatives(
487 | mail_subject,
488 | "hello", # necessary to pass some message here
489 | settings.EMAIL_HOST_USER,
490 | [to_email]
491 | )
492 | email.attach_alternative(message, "text/html")
493 | email.attach(filename, pdf, 'application/pdf')
494 | email.send(fail_silently=False)
495 |
496 | return render(request, 'firstapp/payment/paymentsuccess.html',{'id':order_db.id})
497 | except:
498 | order_db.payment_status = 2
499 | order_db.save()
500 | return render(request, 'firstapp/payment/paymentfailed.html')
501 | else:
502 | order_db.payment_status = 2
503 | order_db.save()
504 | return render(request, 'firstapp/payment/paymentfailed.html')
505 | except:
506 | return HttpResponse("505 not found")
507 |
508 |
509 | class GenerateInvoice(View):
510 | def get(self, request, pk, *args, **kwargs):
511 | try:
512 | order_db = Order.objects.get(id = pk, user = request.user, payment_status = 1) #you can filter using order_id as well
513 | except:
514 | return HttpResponse("505 Not Found")
515 | data = {
516 | 'order_id': order_db.order_id,
517 | 'transaction_id': order_db.razorpay_payment_id,
518 | 'user_email': order_db.user.email,
519 | 'date': str(order_db.datetime_of_payment),
520 | 'name': order_db.user.name,
521 | 'order': order_db,
522 | 'amount': order_db.total_amount,
523 | }
524 | pdf = render_to_pdf('firstapp/payment/invoice.html', data)
525 | #return HttpResponse(pdf, content_type='application/pdf')
526 |
527 | # force download
528 | if pdf:
529 | response = HttpResponse(pdf, content_type='application/pdf')
530 | filename = "Invoice_%s.pdf" %(data['order_id'])
531 | content = "inline; filename='%s'" %(filename)
532 | #download = request.GET.get("download")
533 | #if download:
534 | content = "attachment; filename=%s" %(filename)
535 | response['Content-Disposition'] = content
536 | return response
537 | return HttpResponse("Not found")
538 |
539 |
540 |
541 |
542 |
543 |
544 | # Relating To Group
545 |
546 | # to make a new group you can do that like any other models by creating a new record in it
547 |
548 | # associating a user to a group
549 | from django.contrib.auth.models import Group
550 | @login_required
551 | def addToPremiumGroup(request):
552 | group = Group.objects.get(name="premium")
553 | request.user.groups.add(group)
554 | return HttpResponse("successfully added")
555 |
556 |
557 | # checking group not permission
558 | # in function based view inside the view or "custom decorator"
559 | from .models import PremiumProduct
560 | # from .decorators import group_required
561 | # @group_required('premium')
562 | # def premiumProducts(request):
563 | # #if request.user.groups.filter(name = "premium").exists():
564 | # product = PremiumProduct.objects.all()
565 | # return render(request, "firstapp/listpremiumproducts.html", {"product":product})
566 |
567 | # #else:
568 | # #return HttpResponse("Only for premium members")
569 |
570 | # # in class based view inside the view or a "custom mixin"
571 | # from .mixins import CheckPremiumGroupMixin
572 | # class PremiumProducts(CheckPremiumGroupMixin, ListView):
573 | # template_name = "firstapp/listpremiumproducts.html"
574 | # model = PremiumProduct
575 | # context_object_name = "product"
576 | # #paginate_by = 2
577 |
578 |
579 |
580 | # Relating To Permission
581 | # checking permission and that permission can belong to that user or to the group that user is associated
582 | from django.contrib.auth.decorators import permission_required
583 | @permission_required('firstapp.view_premiumproduct')
584 | def premiumProducts(request):
585 | # ct = ContentType.objects.get_for_model(PremiumProduct)
586 | # if request.user.permissions.filter(codename = "view_premiumproducts" , contentype = ct).exists():
587 |
588 | #if request.user.has_perm('firstapp.view_premiumproduct'):
589 | product = PremiumProduct.objects.all()
590 | return render(request, "firstapp/listpremiumproducts.html", {"product":product})
591 | # else:
592 | # return HttpResponse("Not allowed")
593 |
594 |
595 | from django.contrib.auth.mixins import PermissionRequiredMixin
596 | class PremiumProducts(PermissionRequiredMixin, ListView):
597 | template_name = "firstapp/listpremiumproducts.html"
598 | model = PremiumProduct
599 | context_object_name = "product"
600 | paginate_by = 2
601 | permission_required = "firstapp.view_premiumproduct" # if using PermissionRequiredMixin
602 |
603 |
604 | # for creating permissions
605 | #1 Creating Custom Model Depenedent Permission through Code
606 | #from django.contrib.auth.models import Group, ContentType, Permission
607 | # ct = ContentType.objects.get_for_model(PremiumProduct)
608 | # permission = Permission.objects.create(codename="can_do_this", contentype = ct)
609 |
610 |
611 | #2 Creating Custom Model Dependent Permission by adding in Meta of that model
612 |
613 | #3 Creating Custom Model Independent Permission by creating a separate model for permissions
614 |
615 | # filtering existing permissions
616 | # ct = ContentType.obejcts.get_for_model(PremiumProduct)
617 | # permission = Permission.objects.get(codename='view_premiumproduct', content_type=ct)
618 |
619 |
620 | # Adding permission to user
621 | # user.permissions.add(permission)
622 |
623 | # Adding permission to group
624 | # new_group, created = Group.objects.get_or_create(name="new_group")
625 | # new_group.permissions.add(permission)
626 |
627 |
628 |
629 |
630 |
631 |
632 |
633 |
634 |
635 |
636 |
637 |
638 |
639 |
640 |
641 |
642 |
643 |
644 |
645 |
646 |
647 |
648 |
649 |
650 |
651 |
652 |
653 |
654 |
655 |
656 |
657 |
658 |
659 |
660 |
661 |
662 |
663 |
664 |
665 |
666 |
667 |
--------------------------------------------------------------------------------