├── .gitignore ├── AMAZON API.postman_collection.json ├── Dockerfile ├── LICENSE ├── README.md ├── amazon_backend_api ├── __init__.py ├── admin.py ├── api │ ├── helpers.py │ ├── serializers.py │ ├── urls.py │ ├── utils.py │ └── views.py ├── apps.py ├── manager.py ├── migrations │ ├── 0001_initial.py │ ├── 0002_remove_amazonuser_first_name_and_more.py │ ├── 0003_useraddress.py │ ├── 0004_brand.py │ ├── 0005_category.py │ ├── 0006_subcategory.py │ ├── 0007_size.py │ ├── 0008_product.py │ ├── 0009_productdetail.py │ ├── 0010_color.py │ ├── 0011_productdetail_color.py │ ├── 0012_productdetail_is_stock_productdetail_stocks.py │ ├── 0013_cart.py │ └── __init__.py ├── models.py ├── tests.py └── views.py ├── amz-media ├── brands │ └── logo │ │ ├── download.png │ │ └── hrx.png └── products │ ├── 519I05HaHjL._SX569._SX._UX._SY._UY_.jpg │ ├── 51raYlT-AUL._UX569_.jpg │ ├── 61UagAR6IcL._UX569_.jpg │ ├── 61UagAR6IcL._UX569__xI8CtqU.jpg │ ├── 61mUSVo0b7S._SX569._SX._UX._SY._UY_.jpg │ └── 71mBMP1WTsL._SX569._SX._UX._SY._UY_.jpg ├── backend ├── __init__.py ├── asgi.py ├── settings.py ├── urls.py └── wsgi.py ├── docker-compose.yml ├── manage.py └── requirements.txt /.gitignore: -------------------------------------------------------------------------------- 1 | .env 2 | __pycache__ 3 | .DS_Store 4 | .idea 5 | db.sqlite3 -------------------------------------------------------------------------------- /AMAZON API.postman_collection.json: -------------------------------------------------------------------------------- 1 | { 2 | "info": { 3 | "_postman_id": "e52519e2-f852-456a-a417-3b6d77ad0d15", 4 | "name": "AMAZON API", 5 | "schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json" 6 | }, 7 | "item": [ 8 | { 9 | "name": "Amz Auth", 10 | "item": [ 11 | { 12 | "name": "Register User", 13 | "request": { 14 | "method": "POST", 15 | "header": [], 16 | "body": { 17 | "mode": "raw", 18 | "raw": "{\n \"full_name\" : \"xyz\",\n \"email\" : \"xyz@gmail.com\",\n \"password\" : \"xyz123\"\n}", 19 | "options": { 20 | "raw": { 21 | "language": "json" 22 | } 23 | } 24 | }, 25 | "url": { 26 | "raw": "{{BASEURL}}user/register/", 27 | "host": [ 28 | "{{BASEURL}}user" 29 | ], 30 | "path": [ 31 | "register", 32 | "" 33 | ] 34 | } 35 | }, 36 | "response": [] 37 | }, 38 | { 39 | "name": "Login User", 40 | "request": { 41 | "auth": { 42 | "type": "noauth" 43 | }, 44 | "method": "POST", 45 | "header": [], 46 | "body": { 47 | "mode": "raw", 48 | "raw": "{\n \"email\" : \"xyz@gmail.com\",\n \"password\" : \"xyz123\"\n}", 49 | "options": { 50 | "raw": { 51 | "language": "json" 52 | } 53 | } 54 | }, 55 | "url": { 56 | "raw": "{{BASEURL}}user/sign-in/", 57 | "host": [ 58 | "{{BASEURL}}user" 59 | ], 60 | "path": [ 61 | "sign-in", 62 | "" 63 | ] 64 | } 65 | }, 66 | "response": [] 67 | }, 68 | { 69 | "name": "Get Access token", 70 | "request": { 71 | "auth": { 72 | "type": "noauth" 73 | }, 74 | "method": "POST", 75 | "header": [], 76 | "body": { 77 | "mode": "raw", 78 | "raw": "{\n \"grant_type\" : \"refresh_token\",\n \"refresh_token\" : \"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ0b2tlbl90eXBlIjoicmVmcmVzaCIsImV4cCI6MTcwNzA1MTU3MiwiaWF0IjoxNjc1NTE1NTcyLCJqdGkiOiI1ZmNlNGQ0MDU4ODg0Mzc2OWRkYjY2ZTIzZTJhODRlZCIsImVtYWlsIjoiYWFzaGlzaGt1bWFyMTIzNzZAZ21haWwuY29tIn0.Hk-uG9XMIpGaEJfx7YipwxW9yj2yRxsLEorZiNkYoYE\"\n}", 79 | "options": { 80 | "raw": { 81 | "language": "json" 82 | } 83 | } 84 | }, 85 | "url": { 86 | "raw": "{{BASEURL}}get/access-token/", 87 | "host": [ 88 | "{{BASEURL}}get" 89 | ], 90 | "path": [ 91 | "access-token", 92 | "" 93 | ] 94 | } 95 | }, 96 | "response": [] 97 | } 98 | ] 99 | }, 100 | { 101 | "name": "Amz Address", 102 | "item": [ 103 | { 104 | "name": "Get user address", 105 | "request": { 106 | "method": "GET", 107 | "header": [], 108 | "url": { 109 | "raw": "{{BASE_URL}}user/address/", 110 | "host": [ 111 | "{{BASE_URL}}user" 112 | ], 113 | "path": [ 114 | "address", 115 | "" 116 | ] 117 | } 118 | }, 119 | "response": [] 120 | }, 121 | { 122 | "name": "Create user address", 123 | "request": { 124 | "auth": { 125 | "type": "bearer", 126 | "bearer": [ 127 | { 128 | "key": "token", 129 | "value": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ0b2tlbl90eXBlIjoiYWNjZXNzIiwiZXhwIjoxNjc0OTg1NTY4LCJpYXQiOjE2NzQ5ODE5NjgsImp0aSI6ImY3ODZhZThkY2U4YjRmOWJiYTE3YTRhYzY0Mjg5NmY4IiwiZW1haWwiOiJhc2hAZ21haWwuY29tIn0.3xRAKZf-09w0mNgypmh7Wo7QXWg8xSnGc0QAa9g6s-8", 130 | "type": "string" 131 | } 132 | ] 133 | }, 134 | "method": "POST", 135 | "header": [], 136 | "body": { 137 | "mode": "raw", 138 | "raw": "{\n \"country\": \"India\",\n \"full_name\": \"md, julain\",\n \"mobile_number\": 992737373,\n \"pincode\": 110053,\n \"flat\": \"75\",\n \"street\": \"Block B, 6th avenue, rabibganj\",\n \"landmark\": \"rabibganj hospital\",\n \"town\": \"chittongong\",\n \"state\": \"DL\",\n \"default\": false,\n \"address_type\": \"Home\"\n}", 139 | "options": { 140 | "raw": { 141 | "language": "json" 142 | } 143 | } 144 | }, 145 | "url": { 146 | "raw": "{{BASEURL}}user/address/", 147 | "host": [ 148 | "{{BASEURL}}user" 149 | ], 150 | "path": [ 151 | "address", 152 | "" 153 | ] 154 | } 155 | }, 156 | "response": [] 157 | }, 158 | { 159 | "name": "Delete user address", 160 | "request": { 161 | "auth": { 162 | "type": "bearer", 163 | "bearer": [ 164 | { 165 | "key": "token", 166 | "value": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ0b2tlbl90eXBlIjoiYWNjZXNzIiwiZXhwIjoxNjc1MDc0MTcwLCJpYXQiOjE2NzUwNzA1NzAsImp0aSI6IjU4YTRkMzU4YzBlMDRiZWM4OGM4N2FlZjhiNTM3MmEzIiwiZW1haWwiOiJhc2hAZ21haWwuY29tIn0.o9BvjQhBBxr1wRlRbDQt5Ktxv9bfXFVdsmb2oQpupGc", 167 | "type": "string" 168 | } 169 | ] 170 | }, 171 | "method": "DELETE", 172 | "header": [], 173 | "body": { 174 | "mode": "raw", 175 | "raw": "{\n \"id\" : 37\n}", 176 | "options": { 177 | "raw": { 178 | "language": "json" 179 | } 180 | } 181 | }, 182 | "url": { 183 | "raw": "{{BASEURL}}user/address/", 184 | "host": [ 185 | "{{BASEURL}}user" 186 | ], 187 | "path": [ 188 | "address", 189 | "" 190 | ] 191 | } 192 | }, 193 | "response": [] 194 | }, 195 | { 196 | "name": "Update user address", 197 | "request": { 198 | "auth": { 199 | "type": "bearer", 200 | "bearer": [ 201 | { 202 | "key": "token", 203 | "value": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ0b2tlbl90eXBlIjoiYWNjZXNzIiwiZXhwIjoxNjc1MDc0MTcwLCJpYXQiOjE2NzUwNzA1NzAsImp0aSI6IjU4YTRkMzU4YzBlMDRiZWM4OGM4N2FlZjhiNTM3MmEzIiwiZW1haWwiOiJhc2hAZ21haWwuY29tIn0.o9BvjQhBBxr1wRlRbDQt5Ktxv9bfXFVdsmb2oQpupGc", 204 | "type": "string" 205 | } 206 | ] 207 | }, 208 | "method": "PUT", 209 | "header": [], 210 | "body": { 211 | "mode": "raw", 212 | "raw": "{\n \"id\" : 38,\n \"country\": \"India\",\n \"full_name\": \"Aashish Kumar\",\n \"mobile_number\": 434337332,\n \"pincode\": 110033,\n \"flat\": \"86\",\n \"street\": \"Block k house no 86 jahangir puri\",\n \"landmark\": \"metro station\",\n \"town\": \"Delhi\",\n \"state\": \"DL\",\n \"default\": false,\n \"address_type\": \"Home\"\n}", 213 | "options": { 214 | "raw": { 215 | "language": "json" 216 | } 217 | } 218 | }, 219 | "url": { 220 | "raw": "{{BASEURL}}user/address/", 221 | "host": [ 222 | "{{BASEURL}}user" 223 | ], 224 | "path": [ 225 | "address", 226 | "" 227 | ] 228 | } 229 | }, 230 | "response": [] 231 | }, 232 | { 233 | "name": "Set address default", 234 | "request": { 235 | "auth": { 236 | "type": "bearer", 237 | "bearer": [ 238 | { 239 | "key": "token", 240 | "value": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ0b2tlbl90eXBlIjoiYWNjZXNzIiwiZXhwIjoxNjc1MDU5OTU4LCJpYXQiOjE2NzUwNTYzNTgsImp0aSI6ImUzZWQyYTkzNDNmOTQ0ZWM4MjExNjYxNjNjZDk1ZTZkIiwiZW1haWwiOiJhc2hAZ21haWwuY29tIn0.9P4EC7YLlBDI-4Tq-Iq6-4mdu0d9OTUGof994pJ70XY", 241 | "type": "string" 242 | } 243 | ] 244 | }, 245 | "method": "POST", 246 | "header": [], 247 | "url": { 248 | "raw": "{{BASEURL}}user/address/set/default/38/", 249 | "host": [ 250 | "{{BASEURL}}user" 251 | ], 252 | "path": [ 253 | "address", 254 | "set", 255 | "default", 256 | "38", 257 | "" 258 | ] 259 | } 260 | }, 261 | "response": [] 262 | } 263 | ] 264 | }, 265 | { 266 | "name": "Products", 267 | "item": [ 268 | { 269 | "name": "Get Brands", 270 | "request": { 271 | "method": "GET", 272 | "header": [], 273 | "url": { 274 | "raw": "{{BASEURL}}brands/", 275 | "host": [ 276 | "{{BASEURL}}brands" 277 | ], 278 | "path": [ 279 | "" 280 | ] 281 | } 282 | }, 283 | "response": [] 284 | }, 285 | { 286 | "name": "Create new brand", 287 | "request": { 288 | "method": "POST", 289 | "header": [], 290 | "url": { 291 | "raw": "{{BASEURL}}brands/", 292 | "host": [ 293 | "{{BASEURL}}brands" 294 | ], 295 | "path": [ 296 | "" 297 | ] 298 | } 299 | }, 300 | "response": [] 301 | }, 302 | { 303 | "name": "Get Subcategory1 products", 304 | "request": { 305 | "auth": { 306 | "type": "noauth" 307 | }, 308 | "method": "GET", 309 | "header": [], 310 | "url": { 311 | "raw": "{{BASEURL}}products/fashion/men/", 312 | "host": [ 313 | "{{BASEURL}}products" 314 | ], 315 | "path": [ 316 | "fashion", 317 | "men", 318 | "" 319 | ] 320 | } 321 | }, 322 | "response": [] 323 | }, 324 | { 325 | "name": "Get Subcategory2 products", 326 | "request": { 327 | "auth": { 328 | "type": "noauth" 329 | }, 330 | "method": "GET", 331 | "header": [], 332 | "url": { 333 | "raw": "{{BASEURL}}products/fashion/Men/hoddies/", 334 | "host": [ 335 | "{{BASEURL}}products" 336 | ], 337 | "path": [ 338 | "fashion", 339 | "Men", 340 | "hoddies", 341 | "" 342 | ] 343 | } 344 | }, 345 | "response": [] 346 | }, 347 | { 348 | "name": "Get single product details", 349 | "request": { 350 | "method": "GET", 351 | "header": [], 352 | "url": { 353 | "raw": "{{BASEURL}}product/product-id/1/product-detail/2/", 354 | "host": [ 355 | "{{BASEURL}}product" 356 | ], 357 | "path": [ 358 | "product-id", 359 | "1", 360 | "product-detail", 361 | "2", 362 | "" 363 | ] 364 | } 365 | }, 366 | "response": [] 367 | } 368 | ] 369 | }, 370 | { 371 | "name": "Cart", 372 | "item": [ 373 | { 374 | "name": "Get cart products", 375 | "request": { 376 | "auth": { 377 | "type": "bearer", 378 | "bearer": [ 379 | { 380 | "key": "token", 381 | "value": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ0b2tlbl90eXBlIjoiYWNjZXNzIiwiZXhwIjoxNjc1MzM4ODEyLCJpYXQiOjE2NzUzMzUyMTIsImp0aSI6IjRhZmQ0YzY2MjJkMjRhYzZiY2UyYzQ4NWRhZGJmNmJjIiwiZW1haWwiOiJhYXNoaXNoa3VtYXIxMjM3NkBnbWFpbC5jb20ifQ.ctKnbA1VsMetAyTlAj8VE42N_cNkXTYaegnSK-3jOiI", 382 | "type": "string" 383 | } 384 | ] 385 | }, 386 | "method": "GET", 387 | "header": [], 388 | "url": { 389 | "raw": "{{BASEURL}}cart/", 390 | "host": [ 391 | "{{BASEURL}}cart" 392 | ], 393 | "path": [ 394 | "" 395 | ] 396 | } 397 | }, 398 | "response": [] 399 | }, 400 | { 401 | "name": "Add product to cart", 402 | "request": { 403 | "auth": { 404 | "type": "bearer", 405 | "bearer": [ 406 | { 407 | "key": "token", 408 | "value": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ0b2tlbl90eXBlIjoiYWNjZXNzIiwiZXhwIjoxNjc1MzQzMjI4LCJpYXQiOjE2NzUzMzk2MjgsImp0aSI6ImJiZmI3Yzg1MTgyMzQyNjA4Yjc5NWY3YzJkZTZlYjQ0IiwiZW1haWwiOiJhYXNoaXNoa3VtYXIxMjM3NkBnbWFpbC5jb20ifQ.BczOp0fv6dsLFO6IvCnJuSDe7Motw04NdBxphTT1J8k", 409 | "type": "string" 410 | } 411 | ] 412 | }, 413 | "method": "POST", 414 | "header": [], 415 | "body": { 416 | "mode": "raw", 417 | "raw": "{\n \"product_id\" : 1\n}", 418 | "options": { 419 | "raw": { 420 | "language": "json" 421 | } 422 | } 423 | }, 424 | "url": { 425 | "raw": "{{BASEURL}}cart/", 426 | "host": [ 427 | "{{BASEURL}}cart" 428 | ], 429 | "path": [ 430 | "" 431 | ] 432 | } 433 | }, 434 | "response": [] 435 | }, 436 | { 437 | "name": "Delete product from cart", 438 | "request": { 439 | "auth": { 440 | "type": "bearer", 441 | "bearer": [ 442 | { 443 | "key": "token", 444 | "value": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ0b2tlbl90eXBlIjoiYWNjZXNzIiwiZXhwIjoxNjc1MzQzMjI4LCJpYXQiOjE2NzUzMzk2MjgsImp0aSI6ImJiZmI3Yzg1MTgyMzQyNjA4Yjc5NWY3YzJkZTZlYjQ0IiwiZW1haWwiOiJhYXNoaXNoa3VtYXIxMjM3NkBnbWFpbC5jb20ifQ.BczOp0fv6dsLFO6IvCnJuSDe7Motw04NdBxphTT1J8k", 445 | "type": "string" 446 | } 447 | ] 448 | }, 449 | "method": "DELETE", 450 | "header": [], 451 | "body": { 452 | "mode": "raw", 453 | "raw": "{\n \"product_id\" : 1\n}", 454 | "options": { 455 | "raw": { 456 | "language": "json" 457 | } 458 | } 459 | }, 460 | "url": { 461 | "raw": "{{BASEURL}}cart/", 462 | "host": [ 463 | "{{BASEURL}}cart" 464 | ], 465 | "path": [ 466 | "" 467 | ] 468 | } 469 | }, 470 | "response": [] 471 | }, 472 | { 473 | "name": "Update the product quantity", 474 | "request": { 475 | "auth": { 476 | "type": "bearer", 477 | "bearer": [ 478 | { 479 | "key": "token", 480 | "value": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ0b2tlbl90eXBlIjoiYWNjZXNzIiwiZXhwIjoxNjc1MzQ2OTMyLCJpYXQiOjE2NzUzNDMzMzIsImp0aSI6IjE4YWM5ZjZmZWMyNTQ0ZmE5MWE2YjI3ZWZhMDkwNGNhIiwiZW1haWwiOiJhYXNoaXNoa3VtYXIxMjM3NkBnbWFpbC5jb20ifQ.8saOs82UttJHL0aXWM6_cTnfqZJFh2BVH3ooU8zCCuM", 481 | "type": "string" 482 | } 483 | ] 484 | }, 485 | "method": "PATCH", 486 | "header": [], 487 | "body": { 488 | "mode": "raw", 489 | "raw": "{\n \"id\" : 6,\n \"quantity\" : 50\n}", 490 | "options": { 491 | "raw": { 492 | "language": "json" 493 | } 494 | } 495 | }, 496 | "url": { 497 | "raw": "{{BASEURL}}cart/", 498 | "host": [ 499 | "{{BASEURL}}cart" 500 | ], 501 | "path": [ 502 | "" 503 | ] 504 | } 505 | }, 506 | "response": [] 507 | } 508 | ] 509 | } 510 | ], 511 | "variable": [ 512 | { 513 | "key": "http://localhost:8000/api/amz/", 514 | "value": "BASE_URL" 515 | }, 516 | { 517 | "key": "BASE_URL", 518 | "value": "http://localhost:8000/api/amz/" 519 | }, 520 | { 521 | "key": "BASEURL", 522 | "value": "http://localhost:8001/api/amz/" 523 | } 524 | ] 525 | } -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM python:3.10 2 | 3 | ENV PYTHONUNBUFFERED=1 4 | 5 | WORKDIR /code 6 | 7 | COPY requirements.txt . 8 | 9 | RUN pip install -r requirements.txt 10 | 11 | COPY . . 12 | 13 | CMD ["python","manage.py","runserver","0.0.0.0:8000"] 14 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 Aashishkumar123 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Amazon Clone API 2 | Amazon clone api using django and django rest framework. 3 | 4 | ## Requirements 5 | 6 | Python 3.6+ 7 | 8 | ## Installation 9 | 10 | ```console 11 | $ pip install -r requirements.txt 12 | ``` 13 | ## Database setting 14 | Go to settings.py file and change the database settings. 15 | ``` 16 | DATABASES = { 17 | 'default': { 18 | 'ENGINE': 'django.db.backends.postgresql', 19 | 'NAME': 'your-database-name', 20 | 'USER': 'Your-username', 21 | 'PASSWORD': 'Your-password', 22 | 'HOST': 'localhost', 23 | 'PORT': '5432', 24 | } 25 | } 26 | ``` 27 | and then hit the migrate command to successfully create database table.. 28 | 29 | ```console 30 | $ python manage.py migrate 31 | ``` 32 | ## Run the project 33 | To run the project is very simple just hit this command 34 | ```console 35 | $ python manage.py runserver 36 | ``` 37 | 38 | ## API Documentations 39 | If you want api documentations simple visit this 40 | 41 | swagger docs 42 | ```console 43 | $ http://localhost:8000/api/amz/swagger/ 44 | ``` 45 | ReDocs docs 46 | ```console 47 | $ http://localhost:8000/api/amz/redoc/ 48 | ``` 49 | ## Testing 50 | If you want to run the test cases you can simply run this command 51 | ```console 52 | $ python manage.py test 53 | ``` 54 | Hence all api testing code are wriiten in test.py file. 55 | 56 | ## Code Format 57 | If you want to format the code you can simply run those commands. 58 |

59 | using black package 60 | ```console 61 | $ black amazon_backend_api/api/views.py 62 | ``` 63 | using flake8 package 64 | ```console 65 | $ flake8 amazon_backend_api/api/views.py 66 | ``` 67 | 68 | ## Postman api collection 69 | You can also export the postman api collection and import in postman. 70 | 71 | ## Run server through Docker 72 | If you want to run the server using docker-compose hit the following command. 73 | 74 | ```docker 75 | $ docker-compose up --build 76 | ``` 77 | -------------------------------------------------------------------------------- /amazon_backend_api/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Aashishkumar123/amazon-backend/da4baab5479746141dd66cc4741813d52fed34c8/amazon_backend_api/__init__.py -------------------------------------------------------------------------------- /amazon_backend_api/admin.py: -------------------------------------------------------------------------------- 1 | from django.contrib import admin 2 | from amazon_backend_api.models import ( 3 | Amazonuser, 4 | UserAddress, 5 | Brand, 6 | Category, 7 | Subcategory, 8 | Size, 9 | Product, 10 | ProductDetail, 11 | Color, 12 | Cart, 13 | ) 14 | 15 | admin.site.register(Amazonuser) 16 | admin.site.register(UserAddress) 17 | admin.site.register(Brand) 18 | admin.site.register(Category) 19 | admin.site.register(Subcategory) 20 | admin.site.register(Size) 21 | admin.site.register(Product) 22 | admin.site.register(ProductDetail) 23 | admin.site.register(Color) 24 | admin.site.register(Cart) 25 | -------------------------------------------------------------------------------- /amazon_backend_api/api/helpers.py: -------------------------------------------------------------------------------- 1 | from rest_framework_simplejwt.tokens import RefreshToken 2 | from rest_framework_simplejwt.authentication import JWTAuthentication 3 | from amazon_backend_api.models import Amazonuser 4 | 5 | 6 | JWT_authenticator = JWTAuthentication() 7 | 8 | 9 | def get_tokens_for_user(user): 10 | 11 | refresh = RefreshToken.for_user(user) 12 | 13 | return { 14 | "email": user.email, 15 | "full name": user.full_name, 16 | "refresh": str(refresh), 17 | "access": str(refresh.access_token), 18 | } 19 | 20 | 21 | def get_user_from_token(request): 22 | 23 | response = JWT_authenticator.authenticate(request) 24 | 25 | if response is not None: 26 | user, token = response 27 | return user 28 | 29 | 30 | def get_access_token_from_refresh_token(request): 31 | 32 | if request.data["grant_type"] == "refresh_token": 33 | access_token = RefreshToken(request.data["refresh_token"]).access_token 34 | request.META["HTTP_AUTHORIZATION"] = "Bearer " + str(access_token) 35 | user = get_user_from_token(request) 36 | data = get_tokens_for_user(Amazonuser.objects.get(email=user)) 37 | return data 38 | return [] 39 | -------------------------------------------------------------------------------- /amazon_backend_api/api/serializers.py: -------------------------------------------------------------------------------- 1 | from rest_framework import serializers 2 | from amazon_backend_api.models import ( 3 | Amazonuser, 4 | UserAddress, 5 | Brand, 6 | Product, 7 | ProductDetail, 8 | Size, 9 | Cart, 10 | ) 11 | from rest_framework.serializers import ALL_FIELDS 12 | from django.contrib.auth.hashers import make_password 13 | 14 | 15 | class AmazonuserSerializer(serializers.ModelSerializer): 16 | class Meta: 17 | model = Amazonuser 18 | fields = ["email", "full_name", "password"] 19 | 20 | def create(self, validated_data): 21 | validated_data["password"] = make_password( 22 | validated_data.get("password") 23 | ) 24 | return super(AmazonuserSerializer, self).create(validated_data) 25 | 26 | 27 | class AmazonuserLoginSerializer(serializers.ModelSerializer): 28 | 29 | email = serializers.EmailField() 30 | 31 | class Meta: 32 | model = Amazonuser 33 | fields = ["email", "password"] 34 | 35 | 36 | class AmazonuserAddressSerializer(serializers.ModelSerializer): 37 | 38 | user = serializers.CharField(source="user.email", read_only=True) 39 | 40 | class Meta: 41 | model = UserAddress 42 | fields = ALL_FIELDS 43 | 44 | 45 | class BrandSerializer(serializers.ModelSerializer): 46 | class Meta: 47 | model = Brand 48 | fields = ALL_FIELDS 49 | 50 | 51 | class ProductSerializer(serializers.ModelSerializer): 52 | 53 | brand = serializers.CharField(source="brand.name", read_only=True) 54 | category = serializers.CharField(source="category.name", read_only=True) 55 | subcategory1 = serializers.CharField(source="subcategory1.name", read_only=True) 56 | subcategory2 = serializers.CharField(source="subcategory2.name", read_only=True) 57 | 58 | class Meta: 59 | model = Product 60 | fields = [ 61 | "id", 62 | "name", 63 | "brand", 64 | "category", 65 | "subcategory1", 66 | "subcategory2" 67 | ] 68 | 69 | 70 | class SizeSerializer(serializers.ModelSerializer): 71 | class Meta: 72 | model = Size 73 | fields = ["name"] 74 | 75 | 76 | class ProductDetailsSerializer(serializers.ModelSerializer): 77 | 78 | product = ProductSerializer(read_only=True) 79 | size = SizeSerializer(read_only=True, many=True) 80 | color = serializers.CharField(source="color.code", read_only=True) 81 | 82 | class Meta: 83 | model = ProductDetail 84 | fields = [ 85 | "id", 86 | "product", 87 | "size", 88 | "color", 89 | "description", 90 | "mrp", 91 | "discount", 92 | "stocks", 93 | "image1", 94 | "image2", 95 | "image3", 96 | ] 97 | 98 | 99 | class CartSerializer(serializers.ModelSerializer): 100 | 101 | user = serializers.CharField(source="user.email", read_only=True) 102 | product = ProductDetailsSerializer(read_only=True) 103 | 104 | class Meta: 105 | model = Cart 106 | fields = ["id", "user", "product", "quantity"] 107 | 108 | 109 | class RegenerateAccessTokenSerializer(serializers.Serializer): 110 | 111 | grant_type = serializers.CharField( 112 | error_messages={"required": "grant type may not be blank"} 113 | ) 114 | refresh_token = serializers.CharField( 115 | error_messages={"required": "refresh token may not be blank"} 116 | ) 117 | -------------------------------------------------------------------------------- /amazon_backend_api/api/urls.py: -------------------------------------------------------------------------------- 1 | from django.urls import path, re_path 2 | from amazon_backend_api.api import views 3 | 4 | from rest_framework import permissions 5 | from drf_yasg.views import get_schema_view 6 | from drf_yasg import openapi 7 | 8 | 9 | schema_view = get_schema_view( 10 | openapi.Info( 11 | title="Snippets API", 12 | default_version="v1", 13 | description="Test description", 14 | terms_of_service="https://www.google.com/policies/terms/", 15 | contact=openapi.Contact(email="contact@snippets.local"), 16 | license=openapi.License(name="BSD License"), 17 | ), 18 | public=True, 19 | permission_classes=[permissions.AllowAny], 20 | ) 21 | 22 | 23 | urlpatterns = [ 24 | path("user/register/", views.RegisterAPIView.as_view(), name="amz-user-register"), 25 | path("user/sign-in/", views.SigninAPIView.as_view(), name="amz-user-sign-in"), 26 | path( 27 | "get/access-token/", 28 | views.RegenerateAccessToken.as_view(), 29 | name="amz-get-access-token", 30 | ), 31 | path("user/address/", views.UserAddressAPIView.as_view(), name="amz-user-address"), 32 | path( 33 | "user/address/set/default//", 34 | views.SetdefaultAddressAPIView.as_view(), 35 | name="amz-user-address-set-default", 36 | ), 37 | path("brands/", views.BrandAPIView.as_view(), name="amz-brands"), 38 | path( 39 | "products/fashion//", 40 | views.ProductsAPIView.as_view(), 41 | name="amz-products-subcategory1", 42 | ), 43 | path( 44 | "products/fashion///", 45 | views.ProductsAPIView.as_view(), 46 | name="amz-products-subcategory2", 47 | ), 48 | path( 49 | "product/product-id//product-detail//", 50 | views.ProductDetailsAPIView.as_view(), 51 | name="amz-product-details", 52 | ), 53 | path("cart/", views.CartAPIView.as_view(), name="amz-cart"), 54 | re_path( 55 | r"^swagger(?P\.json|\.yaml)$", 56 | schema_view.without_ui(cache_timeout=0), 57 | name="schema-json", 58 | ), 59 | re_path( 60 | r"^swagger/$", 61 | schema_view.with_ui("swagger", cache_timeout=0), 62 | name="schema-swagger-ui", 63 | ), 64 | re_path( 65 | r"^redoc/$", schema_view.with_ui("redoc", cache_timeout=0), name="schema-redoc" 66 | ), 67 | ] 68 | -------------------------------------------------------------------------------- /amazon_backend_api/api/utils.py: -------------------------------------------------------------------------------- 1 | INDIAN_STATES = { 2 | "AN":"Andaman and Nicobar Islands", 3 | "AP":"Andhra Pradesh", 4 | "AR":"Arunachal Pradesh", 5 | "AS":"Assam", 6 | "BR":"Bihar", 7 | "CG":"Chandigarh", 8 | "CH":"Chhattisgarh", 9 | "DN":"Dadra and Nagar Haveli", 10 | "DD":"Daman and Diu", 11 | "DL":"Delhi", 12 | "GA":"Goa", 13 | "GJ":"Gujarat", 14 | "HR":"Haryana", 15 | "HP":"Himachal Pradesh", 16 | "JK":"Jammu and Kashmir", 17 | "JH":"Jharkhand", 18 | "KA":"Karnataka", 19 | "KL":"Kerala", 20 | "LA":"Ladakh", 21 | "LD":"Lakshadweep", 22 | "MP":"Madhya Pradesh", 23 | "MH":"Maharashtra", 24 | "MN":"Manipur", 25 | "ML":"Meghalaya", 26 | "MZ":"Mizoram", 27 | "NL":"Nagaland", 28 | "OR":"Odisha", 29 | "PY":"Puducherry", 30 | "PB":"Punjab", 31 | "RJ":"Rajasthan", 32 | "SK":"Sikkim", 33 | "TN":"Tamil Nadu", 34 | "TS":"Telangana", 35 | "TR":"Tripura", 36 | "UP":"Uttar Pradesh", 37 | "UK":"Uttarakhand", 38 | "WB":"West Bengal" 39 | } 40 | -------------------------------------------------------------------------------- /amazon_backend_api/api/views.py: -------------------------------------------------------------------------------- 1 | from django.contrib.auth import authenticate 2 | from rest_framework import status 3 | from rest_framework.permissions import IsAuthenticated 4 | from rest_framework.response import Response 5 | from rest_framework.views import APIView 6 | 7 | from amazon_backend_api.api.helpers import ( 8 | get_access_token_from_refresh_token, 9 | get_tokens_for_user, 10 | get_user_from_token, 11 | ) 12 | from amazon_backend_api.api.serializers import ( 13 | AmazonuserAddressSerializer, 14 | AmazonuserLoginSerializer, 15 | AmazonuserSerializer, 16 | BrandSerializer, 17 | CartSerializer, 18 | ProductDetailsSerializer, 19 | RegenerateAccessTokenSerializer, 20 | ) 21 | from amazon_backend_api.models import ( 22 | Amazonuser, 23 | Brand, 24 | Cart, 25 | Product, 26 | ProductDetail, 27 | Subcategory, 28 | UserAddress, 29 | ) 30 | 31 | 32 | class RegisterAPIView(APIView): 33 | """This api used for register new user""" 34 | 35 | def post(self, request): 36 | """Handle post request""" 37 | 38 | serializer = AmazonuserSerializer(data=request.data) 39 | 40 | """it will check data validation""" 41 | if serializer.is_valid(): 42 | 43 | """create a new user""" 44 | amz_user = serializer.save() 45 | 46 | """return the user information and tokens""" 47 | res_data = get_tokens_for_user(Amazonuser.objects.get(email=amz_user.email)) 48 | 49 | """response result""" 50 | response = { 51 | "status": status.HTTP_201_CREATED, 52 | "message": "created", 53 | "data": res_data, 54 | } 55 | return Response(response, status=status.HTTP_201_CREATED) 56 | 57 | """if validation failed it return this""" 58 | response = { 59 | "status": status.HTTP_400_BAD_REQUEST, 60 | "message": "bad request", 61 | "data": serializer.errors, 62 | } 63 | return Response(response, status=status.HTTP_400_BAD_REQUEST) 64 | 65 | 66 | class SigninAPIView(APIView): 67 | """""This api is handling login""" "" 68 | 69 | def post(self, request): 70 | """Handle post request""" 71 | 72 | login_serializer = AmazonuserLoginSerializer(data=request.data) 73 | 74 | """it will check data validation""" 75 | if login_serializer.is_valid(): 76 | 77 | """get email and password""" 78 | email = login_serializer.validated_data["email"] 79 | password = login_serializer.validated_data["password"] 80 | 81 | """it will check the credentials is valid or not""" 82 | user = authenticate(request, email=email, password=password) 83 | 84 | """it will return access token if credentials are ok""" 85 | if user is not None: 86 | 87 | """return the user information and tokens""" 88 | res_data = get_tokens_for_user(Amazonuser.objects.get(email=email)) 89 | response = { 90 | "status": status.HTTP_200_OK, 91 | "message": "success", 92 | "data": res_data, 93 | } 94 | return Response(response, status=status.HTTP_200_OK) 95 | else: 96 | """otherwise return this response""" 97 | response = { 98 | "status": status.HTTP_401_UNAUTHORIZED, 99 | "message": "Invalid Email or Password", 100 | } 101 | return Response(response, status=status.HTTP_401_UNAUTHORIZED) 102 | 103 | """if validation failed it return this""" 104 | response = { 105 | "status": status.HTTP_400_BAD_REQUEST, 106 | "message": "bad request", 107 | "data": login_serializer.errors, 108 | } 109 | return Response(response, status=status.HTTP_400_BAD_REQUEST) 110 | 111 | 112 | class RegenerateAccessToken(APIView): 113 | """This api is used to regenerate access token""" 114 | 115 | def post(self, request): 116 | """Handle post request""" 117 | 118 | reg_access_token_serializer = RegenerateAccessTokenSerializer(data=request.data) 119 | 120 | """it will check data validation""" 121 | if reg_access_token_serializer.is_valid(): 122 | 123 | """return access token""" 124 | data = get_access_token_from_refresh_token(request) 125 | response = { 126 | "status": status.HTTP_200_OK, 127 | "message": "success", 128 | "data": data, 129 | } 130 | return Response(response, status=status.HTTP_200_OK) 131 | 132 | """if validation failed it return this""" 133 | response = { 134 | "status": status.HTTP_400_BAD_REQUEST, 135 | "message": "bad request", 136 | "data": reg_access_token_serializer.errors, 137 | } 138 | return Response(response, status=status.HTTP_400_BAD_REQUEST) 139 | 140 | 141 | class UserAddressAPIView(APIView): 142 | """This api is used to get , update , delete , create the user address""" 143 | 144 | permission_classes = [IsAuthenticated] 145 | 146 | def get(self, request): 147 | print(request.user) 148 | user = get_user_from_token(request) 149 | address = UserAddress.objects.filter(user=user) 150 | serialize_address = AmazonuserAddressSerializer(address, many=True) 151 | response = { 152 | "status": status.HTTP_200_OK, 153 | "message": "OK", 154 | "data": serialize_address.data, 155 | } 156 | return Response(response, status=status.HTTP_200_OK) 157 | 158 | def post(self, request): 159 | data = request.data 160 | user = get_user_from_token(request) 161 | serialize_address = AmazonuserAddressSerializer(data=data) 162 | if serialize_address.is_valid(): 163 | created_adrs = serialize_address.save(user=user) 164 | response = { 165 | "status": status.HTTP_201_CREATED, 166 | "message": "address created", 167 | "data": AmazonuserAddressSerializer( 168 | UserAddress.objects.get(id=created_adrs.id) 169 | ).data, 170 | } 171 | return Response(response, status=status.HTTP_201_CREATED) 172 | response = { 173 | "status": status.HTTP_400_BAD_REQUEST, 174 | "message": "bad request", 175 | "data": serialize_address.errors, 176 | } 177 | return Response(response, status=status.HTTP_400_BAD_REQUEST) 178 | 179 | def put(self, request): 180 | user = get_user_from_token(request) 181 | if "id" not in request.data: 182 | response = { 183 | "status": "400", 184 | "message": "bad request", 185 | "data": {"id": ["This field is required."]}, 186 | } 187 | return Response(response, status=status.HTTP_400_BAD_REQUEST) 188 | data = request.data 189 | 190 | try: 191 | user_address = UserAddress.objects.filter(user=user) 192 | adrs_id = user_address.get(id=data["id"]) 193 | except UserAddress.DoesNotExist: 194 | response = { 195 | "status": status.HTTP_400_BAD_REQUEST, 196 | "message": "Id does not exist.", 197 | } 198 | return Response(response, status=status.HTTP_400_BAD_REQUEST) 199 | 200 | serialize_address = AmazonuserAddressSerializer(adrs_id, data=data) 201 | if serialize_address.is_valid(): 202 | updated_adrs = serialize_address.save() 203 | response = { 204 | "status": status.HTTP_200_OK, 205 | "message": "address updated", 206 | "data": AmazonuserAddressSerializer( 207 | UserAddress.objects.get(id=updated_adrs.id) 208 | ).data, 209 | } 210 | return Response(response, status=status.HTTP_200_OK) 211 | response = { 212 | "status": status.HTTP_400_BAD_REQUEST, 213 | "message": "bad request", 214 | "data": serialize_address.errors, 215 | } 216 | return Response(response, status=status.HTTP_400_BAD_REQUEST) 217 | 218 | def delete(self, request): 219 | user = get_user_from_token(request) 220 | if "id" not in request.data or not request.data["id"]: 221 | response = { 222 | "status": status.HTTP_400_BAD_REQUEST, 223 | "message": "bad request", 224 | "data": {"id": ["This field is required."]}, 225 | } 226 | return Response(response, status=status.HTTP_400_BAD_REQUEST) 227 | 228 | address_id = request.data["id"] 229 | 230 | try: 231 | UserAddress.objects.filter(user=user).get(id=address_id).delete() 232 | except UserAddress.DoesNotExist: 233 | response = { 234 | "status": status.HTTP_400_BAD_REQUEST, 235 | "message": "Id does not exist.", 236 | } 237 | return Response(response, status=status.HTTP_400_BAD_REQUEST) 238 | 239 | response = {"status": status.HTTP_204_NO_CONTENT, "message": "Address deleted."} 240 | return Response(response, status=status.HTTP_204_NO_CONTENT) 241 | 242 | 243 | """ 244 | This api will set a default address.... 245 | """ 246 | 247 | 248 | class SetdefaultAddressAPIView(APIView): 249 | 250 | permission_classes = [IsAuthenticated] 251 | 252 | def post(self, request, id): 253 | user = get_user_from_token(request) 254 | df_adrs = UserAddress.objects.filter(user=user) 255 | df_adrs.filter(default=True).update(default=False) 256 | 257 | try: 258 | update_default_adrs = df_adrs.get(id=id) 259 | update_default_adrs.default = True 260 | update_default_adrs.save() 261 | except UserAddress.DoesNotExist: 262 | response = {"status": status.HTTP_404_NOT_FOUND, "message": "ID not found"} 263 | return Response(response, status=status.HTTP_404_NOT_FOUND) 264 | 265 | response = {"status": status.HTTP_200_OK, "message": "success"} 266 | return Response(response, status=status.HTTP_200_OK) 267 | 268 | 269 | """ 270 | This api will GET and Save the new brands 271 | """ 272 | 273 | 274 | class BrandAPIView(APIView): 275 | 276 | permission_classes = [IsAuthenticated] 277 | 278 | def get(self, request): 279 | brands = Brand.objects.all() 280 | brand_serializer = BrandSerializer(brands, many=True) 281 | response = { 282 | "status": status.HTTP_200_OK, 283 | "message": "success", 284 | "data": brand_serializer.data, 285 | } 286 | return Response(response, status=status.HTTP_200_OK) 287 | 288 | def post(self, request): 289 | data = request.data 290 | brand_serializer = BrandSerializer(data=data) 291 | if brand_serializer.is_valid(): 292 | brand = brand_serializer.save() 293 | response = { 294 | "status": status.HTTP_201_CREATED, 295 | "message": "brand created", 296 | "data": BrandSerializer(Brand.objects.get(id=brand.id)).data, 297 | } 298 | return Response(response, status=status.HTTP_201_CREATED) 299 | response = { 300 | "status": status.HTTP_400_BAD_REQUEST, 301 | "message": "bad request", 302 | "data": brand_serializer.errors, 303 | } 304 | return Response(response, status=status.HTTP_400_BAD_REQUEST) 305 | 306 | 307 | class ProductsAPIView(APIView): 308 | """ 309 | This api return all the products on the base of its subcategory 310 | """ 311 | 312 | def get(self, request, subcategory1, subcategory2=None): 313 | if subcategory2: 314 | products = Product.objects.filter( 315 | subcategory1=Subcategory.objects.filter( 316 | name__iexact=subcategory1 317 | ).first(), 318 | subcategory2=Subcategory.objects.filter( 319 | name__iexact=subcategory2 320 | ).first(), 321 | ).first() 322 | else: 323 | products = Product.objects.filter( 324 | subcategory1=Subcategory.objects.filter( 325 | name__iexact=subcategory1 326 | ).first() 327 | ).first() 328 | 329 | allproducts = ProductDetail.objects.filter(product=products) 330 | allproducts_serializer = ProductDetailsSerializer(allproducts, many=True) 331 | 332 | response = { 333 | "status": status.HTTP_200_OK, 334 | "message": "success", 335 | "data": allproducts_serializer.data, 336 | } 337 | return Response(response, status=status.HTTP_200_OK) 338 | 339 | 340 | class ProductDetailsAPIView(APIView): 341 | """ 342 | This api return single product details 343 | """ 344 | 345 | def get(self, request, product_id, product_detail_id): 346 | try: 347 | product = Product.objects.get(id=product_id) 348 | except Product.DoesNotExist: 349 | response = { 350 | "status": status.HTTP_400_BAD_REQUEST, 351 | "message": "product not found.", 352 | } 353 | return Response(response, status=status.HTTP_400_BAD_REQUEST) 354 | 355 | product_details = ProductDetail.objects.filter(product=product) 356 | 357 | if not product_details.filter(id=product_detail_id): 358 | response = { 359 | "status": status.HTTP_400_BAD_REQUEST, 360 | "message": "product not found.", 361 | "data": [], 362 | } 363 | return Response(response, status=status.HTTP_400_BAD_REQUEST) 364 | 365 | product_details_serializer = ProductDetailsSerializer( 366 | product_details, many=True 367 | ) 368 | 369 | response = { 370 | "status": status.HTTP_200_OK, 371 | "message": "success", 372 | "active_product_detail_id": product_detail_id, 373 | "data": product_details_serializer.data, 374 | } 375 | return Response(response, status=status.HTTP_200_OK) 376 | 377 | 378 | class CartAPIView(APIView): 379 | """ 380 | cart api that will get all the products of an user 381 | cart api that will add product to the cart of an user 382 | cart api that will update the product quantity of an user 383 | cart api that will remove the product from cart of an user 384 | """ 385 | 386 | permission_classes = [IsAuthenticated] 387 | 388 | """ 389 | Return all the products from cart for particular user 390 | """ 391 | 392 | def get(self, request): 393 | user = get_user_from_token(request) 394 | cart = Cart.objects.filter(user=user) 395 | cart_serializer = CartSerializer(cart, many=True) 396 | response = { 397 | "status": status.HTTP_200_OK, 398 | "message": "success", 399 | "data": cart_serializer.data, 400 | } 401 | return Response(response, status=status.HTTP_200_OK) 402 | 403 | """ 404 | Add the product to cart for a particular user 405 | """ 406 | 407 | def post(self, request): 408 | user = get_user_from_token(request) 409 | data = request.data 410 | 411 | """ 412 | This will response 404 if product_id not in request data 413 | """ 414 | if "product_id" not in data: 415 | response = { 416 | "status": status.HTTP_400_BAD_REQUEST, 417 | "message": "bad request", 418 | "data": {"product_id": "Product id should not empty"}, 419 | } 420 | return Response(response, status=status.HTTP_400_BAD_REQUEST) 421 | 422 | """ 423 | This will response 404 status code if get wrong product id 424 | """ 425 | try: 426 | product_id = ProductDetail.objects.get(id=request.data["product_id"]) 427 | except ProductDetail.DoesNotExist: 428 | response = { 429 | "status": status.HTTP_400_BAD_REQUEST, 430 | "message": "bad request", 431 | "data": {"product_id": "Invalid product id"}, 432 | } 433 | return Response(response, status=status.HTTP_400_BAD_REQUEST) 434 | 435 | """ 436 | This will check product already in cart then will response 404 status code 437 | """ 438 | if Cart.objects.filter(user=user, product=data["product_id"]).exists(): 439 | response = { 440 | "status": status.HTTP_400_BAD_REQUEST, 441 | "message": "bad request", 442 | "data": {"product": "Product already in cart"}, 443 | } 444 | return Response(response, status=status.HTTP_400_BAD_REQUEST) 445 | 446 | """ 447 | If everything fine this will add product to cart 448 | """ 449 | cart_serializer = CartSerializer(data=data) 450 | if cart_serializer.is_valid(): 451 | cart_serializer.save(user=user, product=product_id) 452 | response = { 453 | "status": status.HTTP_201_CREATED, 454 | "message": "proudct added to cart", 455 | } 456 | return Response(response, status=status.HTTP_400_BAD_REQUEST) 457 | 458 | """ 459 | else return 404 status code 460 | """ 461 | response = { 462 | "status": status.HTTP_400_BAD_REQUEST, 463 | "message": "bad request", 464 | "data": cart_serializer.errors, 465 | } 466 | return Response(response, status=status.HTTP_400_BAD_REQUEST) 467 | 468 | """ 469 | This will update the product quantity of a particular user 470 | """ 471 | 472 | def patch(self, request): 473 | data = request.data 474 | user = get_user_from_token(request) 475 | 476 | """ 477 | This will check request data is having id or not 478 | """ 479 | if "id" not in data: 480 | response = { 481 | "status": status.HTTP_400_BAD_REQUEST, 482 | "message": "bad request", 483 | "data": {"id": ["id is required"]}, 484 | } 485 | return Response(response, status=status.HTTP_400_BAD_REQUEST) 486 | 487 | """ 488 | This will check the cart id is belong the authenticated user 489 | """ 490 | if not Cart.objects.filter(user=user, id=data["id"]).exists(): 491 | response = { 492 | "status": status.HTTP_400_BAD_REQUEST, 493 | "message": "bad request", 494 | "data": {"id": ["invalid cart id"]}, 495 | } 496 | return Response(response, status=status.HTTP_400_BAD_REQUEST) 497 | 498 | """ 499 | This will check the validation and update the product quantity 500 | """ 501 | cart_serializer = CartSerializer( 502 | instance=Cart.objects.get(id=data["id"]), data=data, partial=True 503 | ) 504 | if cart_serializer.is_valid(): 505 | cart_serializer.save() 506 | response = { 507 | "status": status.HTTP_202_ACCEPTED, 508 | "message": "quantity updated", 509 | "data": CartSerializer( 510 | Cart.objects.get(id=cart_serializer.data["id"]) 511 | ).data, 512 | } 513 | return Response(response, status=status.HTTP_202_ACCEPTED) 514 | 515 | """ 516 | otherwise it raise the validation 517 | """ 518 | response = { 519 | "status": status.HTTP_400_BAD_REQUEST, 520 | "message": "bad request", 521 | "data": cart_serializer.errors, 522 | } 523 | return Response(response, status=status.HTTP_400_BAD_REQUEST) 524 | 525 | """ 526 | This will remove product from cart of a particular user 527 | """ 528 | 529 | def delete(self, request): 530 | data = request.data 531 | user = get_user_from_token(request) 532 | """ 533 | This will response 404 if product_id not in request data 534 | """ 535 | if "product_id" not in data: 536 | response = { 537 | "status": status.HTTP_400_BAD_REQUEST, 538 | "message": "bad request", 539 | "data": {"product_id": "Product id should not empty"}, 540 | } 541 | return Response(response, status=status.HTTP_400_BAD_REQUEST) 542 | 543 | """ 544 | This will check product avaliable in cart to delete 545 | """ 546 | if Cart.objects.filter(user=user, product=data["product_id"]).exists(): 547 | Cart.objects.get(product=data["product_id"]).delete() 548 | response = { 549 | "status": status.HTTP_204_NO_CONTENT, 550 | "message": "product remove from cart", 551 | } 552 | return Response(response, status=status.HTTP_204_NO_CONTENT) 553 | 554 | """ 555 | other response inavlid product id 556 | """ 557 | response = { 558 | "status": status.HTTP_400_BAD_REQUEST, 559 | "message": "bad request", 560 | "data": {"product_id": "Invalid product id"}, 561 | } 562 | return Response(response, status=status.HTTP_400_BAD_REQUEST) 563 | -------------------------------------------------------------------------------- /amazon_backend_api/apps.py: -------------------------------------------------------------------------------- 1 | from django.apps import AppConfig 2 | 3 | 4 | class AmazonBackendApiConfig(AppConfig): 5 | default_auto_field = "django.db.models.BigAutoField" 6 | name = "amazon_backend_api" 7 | -------------------------------------------------------------------------------- /amazon_backend_api/manager.py: -------------------------------------------------------------------------------- 1 | from django.contrib.auth.base_user import BaseUserManager 2 | from django.utils.translation import gettext_lazy as _ 3 | 4 | 5 | class AmazonuserManager(BaseUserManager): 6 | """ 7 | Custom user model manager where email is the unique identifiers 8 | for authentication instead of usernames. 9 | """ 10 | 11 | def create_user(self, email, password, **extra_fields): 12 | """ 13 | Create and save a User with the given email and password. 14 | """ 15 | if not email: 16 | raise ValueError(_("The Email must be set")) 17 | email = self.normalize_email(email) 18 | user = self.model(email=email, **extra_fields) 19 | user.set_password(password) 20 | user.save() 21 | return user 22 | 23 | def create_superuser(self, email, password, **extra_fields): 24 | """ 25 | Create and save a SuperUser with the given email and password. 26 | """ 27 | extra_fields.setdefault("is_staff", True) 28 | extra_fields.setdefault("is_superuser", True) 29 | extra_fields.setdefault("is_active", True) 30 | 31 | if extra_fields.get("is_staff") is not True: 32 | raise ValueError(_("Superuser must have is_staff=True.")) 33 | if extra_fields.get("is_superuser") is not True: 34 | raise ValueError(_("Superuser must have is_superuser=True.")) 35 | return self.create_user(email, password, **extra_fields) 36 | -------------------------------------------------------------------------------- /amazon_backend_api/migrations/0001_initial.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.2 on 2023-01-27 11:32 2 | 3 | from django.db import migrations, models 4 | import django.utils.timezone 5 | 6 | 7 | class Migration(migrations.Migration): 8 | 9 | initial = True 10 | 11 | dependencies = [ 12 | ('auth', '0012_alter_user_first_name_max_length'), 13 | ] 14 | 15 | operations = [ 16 | migrations.CreateModel( 17 | name='Amazonuser', 18 | fields=[ 19 | ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), 20 | ('password', models.CharField(max_length=128, verbose_name='password')), 21 | ('last_login', models.DateTimeField(blank=True, null=True, verbose_name='last login')), 22 | ('is_superuser', models.BooleanField(default=False, help_text='Designates that this user has all permissions without explicitly assigning them.', verbose_name='superuser status')), 23 | ('first_name', models.CharField(blank=True, max_length=150, verbose_name='first name')), 24 | ('last_name', models.CharField(blank=True, max_length=150, verbose_name='last name')), 25 | ('is_staff', models.BooleanField(default=False, help_text='Designates whether the user can log into this admin site.', verbose_name='staff status')), 26 | ('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')), 27 | ('date_joined', models.DateTimeField(default=django.utils.timezone.now, verbose_name='date joined')), 28 | ('email', models.EmailField(max_length=254, unique=True, verbose_name='email address')), 29 | ('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')), 30 | ('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')), 31 | ], 32 | options={ 33 | 'verbose_name': 'user', 34 | 'verbose_name_plural': 'users', 35 | 'abstract': False, 36 | }, 37 | ), 38 | ] 39 | -------------------------------------------------------------------------------- /amazon_backend_api/migrations/0002_remove_amazonuser_first_name_and_more.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 4.1.5 on 2023-01-28 04:54 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ("amazon_backend_api", "0001_initial"), 10 | ] 11 | 12 | operations = [ 13 | migrations.RemoveField(model_name="amazonuser", name="first_name",), 14 | migrations.RemoveField(model_name="amazonuser", name="last_name",), 15 | migrations.AddField( 16 | model_name="amazonuser", 17 | name="full_name", 18 | field=models.CharField( 19 | default="", max_length=100, verbose_name="full name" 20 | ), 21 | preserve_default=False, 22 | ), 23 | ] 24 | -------------------------------------------------------------------------------- /amazon_backend_api/migrations/0003_useraddress.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 4.1.5 on 2023-01-28 09:20 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 | dependencies = [ 11 | ("amazon_backend_api", "0002_remove_amazonuser_first_name_and_more"), 12 | ] 13 | 14 | operations = [ 15 | migrations.CreateModel( 16 | name="UserAddress", 17 | fields=[ 18 | ( 19 | "id", 20 | models.BigAutoField( 21 | auto_created=True, 22 | primary_key=True, 23 | serialize=False, 24 | verbose_name="ID", 25 | ), 26 | ), 27 | ("created_at", models.DateTimeField(auto_now_add=True, null=True)), 28 | ("updated_at", models.DateTimeField(auto_now=True, null=True)), 29 | ( 30 | "country", 31 | models.CharField( 32 | choices=[("India", "India")], 33 | max_length=100, 34 | verbose_name="Country", 35 | ), 36 | ), 37 | ( 38 | "full_name", 39 | models.CharField( 40 | max_length=100, verbose_name="Full name (First and last name)" 41 | ), 42 | ), 43 | ("mobile_number", models.IntegerField(verbose_name="Mobile number")), 44 | ("pincode", models.IntegerField(verbose_name="Pincode")), 45 | ( 46 | "flat", 47 | models.CharField( 48 | max_length=200, 49 | verbose_name="Flat, House no., Building, Company, Apartment", 50 | ), 51 | ), 52 | ( 53 | "street", 54 | models.CharField( 55 | max_length=200, verbose_name="Area, Street, Sector, Village" 56 | ), 57 | ), 58 | ("landmark", models.CharField(max_length=100, verbose_name="Landmark")), 59 | ("town", models.CharField(max_length=100, verbose_name="Town/City")), 60 | ( 61 | "state", 62 | models.CharField( 63 | choices=[ 64 | ("AN", "Andaman and Nicobar Islands"), 65 | ("AP", "Andhra Pradesh"), 66 | ("AR", "Arunachal Pradesh"), 67 | ("AS", "Assam"), 68 | ("BR", "Bihar"), 69 | ("CG", "Chandigarh"), 70 | ("CH", "Chhattisgarh"), 71 | ("DN", "Dadra and Nagar Haveli"), 72 | ("DD", "Daman and Diu"), 73 | ("DL", "Delhi"), 74 | ("GA", "Goa"), 75 | ("GJ", "Gujarat"), 76 | ("HR", "Haryana"), 77 | ("HP", "Himachal Pradesh"), 78 | ("JK", "Jammu and Kashmir"), 79 | ("JH", "Jharkhand"), 80 | ("KA", "Karnataka"), 81 | ("KL", "Kerala"), 82 | ("LA", "Ladakh"), 83 | ("LD", "Lakshadweep"), 84 | ("MP", "Madhya Pradesh"), 85 | ("MH", "Maharashtra"), 86 | ("MN", "Manipur"), 87 | ("ML", "Meghalaya"), 88 | ("MZ", "Mizoram"), 89 | ("NL", "Nagaland"), 90 | ("OR", "Odisha"), 91 | ("PY", "Puducherry"), 92 | ("PB", "Punjab"), 93 | ("RJ", "Rajasthan"), 94 | ("SK", "Sikkim"), 95 | ("TN", "Tamil Nadu"), 96 | ("TS", "Telangana"), 97 | ("TR", "Tripura"), 98 | ("UP", "Uttar Pradesh"), 99 | ("UK", "Uttarakhand"), 100 | ("WB", "West Bengal"), 101 | ], 102 | max_length=100, 103 | verbose_name="State", 104 | ), 105 | ), 106 | ( 107 | "default", 108 | models.BooleanField( 109 | default=False, verbose_name="Make this my default address" 110 | ), 111 | ), 112 | ( 113 | "address_type", 114 | models.CharField( 115 | choices=[("Home", "Home"), ("Office", "Office")], 116 | max_length=100, 117 | verbose_name="Address Type", 118 | ), 119 | ), 120 | ( 121 | "user", 122 | models.ForeignKey( 123 | on_delete=django.db.models.deletion.CASCADE, 124 | to=settings.AUTH_USER_MODEL, 125 | verbose_name="User", 126 | ), 127 | ), 128 | ], 129 | options={"abstract": False,}, 130 | ), 131 | ] 132 | -------------------------------------------------------------------------------- /amazon_backend_api/migrations/0004_brand.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 4.1.5 on 2023-02-01 11:12 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ("amazon_backend_api", "0003_useraddress"), 10 | ] 11 | 12 | operations = [ 13 | migrations.CreateModel( 14 | name="Brand", 15 | fields=[ 16 | ( 17 | "id", 18 | models.BigAutoField( 19 | auto_created=True, 20 | primary_key=True, 21 | serialize=False, 22 | verbose_name="ID", 23 | ), 24 | ), 25 | ("created_at", models.DateTimeField(auto_now_add=True, null=True)), 26 | ("updated_at", models.DateTimeField(auto_now=True, null=True)), 27 | ("name", models.CharField(max_length=100, verbose_name="Brand Name")), 28 | ("logo", models.ImageField(upload_to="brands/logo")), 29 | ], 30 | options={"abstract": False,}, 31 | ), 32 | ] 33 | -------------------------------------------------------------------------------- /amazon_backend_api/migrations/0005_category.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 4.1.5 on 2023-02-01 12:07 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ("amazon_backend_api", "0004_brand"), 10 | ] 11 | 12 | operations = [ 13 | migrations.CreateModel( 14 | name="Category", 15 | fields=[ 16 | ( 17 | "id", 18 | models.BigAutoField( 19 | auto_created=True, 20 | primary_key=True, 21 | serialize=False, 22 | verbose_name="ID", 23 | ), 24 | ), 25 | ("created_at", models.DateTimeField(auto_now_add=True, null=True)), 26 | ("updated_at", models.DateTimeField(auto_now=True, null=True)), 27 | ( 28 | "name", 29 | models.CharField(max_length=100, verbose_name="Category Name"), 30 | ), 31 | ], 32 | options={"abstract": False,}, 33 | ), 34 | ] 35 | -------------------------------------------------------------------------------- /amazon_backend_api/migrations/0006_subcategory.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 4.1.5 on 2023-02-01 12:09 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ("amazon_backend_api", "0005_category"), 10 | ] 11 | 12 | operations = [ 13 | migrations.CreateModel( 14 | name="Subcategory", 15 | fields=[ 16 | ( 17 | "id", 18 | models.BigAutoField( 19 | auto_created=True, 20 | primary_key=True, 21 | serialize=False, 22 | verbose_name="ID", 23 | ), 24 | ), 25 | ("created_at", models.DateTimeField(auto_now_add=True, null=True)), 26 | ("updated_at", models.DateTimeField(auto_now=True, null=True)), 27 | ( 28 | "name", 29 | models.CharField(max_length=100, verbose_name="SubCategory Name"), 30 | ), 31 | ], 32 | options={"abstract": False,}, 33 | ), 34 | ] 35 | -------------------------------------------------------------------------------- /amazon_backend_api/migrations/0007_size.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 4.1.5 on 2023-02-01 12:16 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ("amazon_backend_api", "0006_subcategory"), 10 | ] 11 | 12 | operations = [ 13 | migrations.CreateModel( 14 | name="Size", 15 | fields=[ 16 | ( 17 | "id", 18 | models.BigAutoField( 19 | auto_created=True, 20 | primary_key=True, 21 | serialize=False, 22 | verbose_name="ID", 23 | ), 24 | ), 25 | ("created_at", models.DateTimeField(auto_now_add=True, null=True)), 26 | ("updated_at", models.DateTimeField(auto_now=True, null=True)), 27 | ("name", models.CharField(max_length=100, verbose_name="Size")), 28 | ], 29 | options={"abstract": False,}, 30 | ), 31 | ] 32 | -------------------------------------------------------------------------------- /amazon_backend_api/migrations/0008_product.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 4.1.5 on 2023-02-01 13:07 2 | 3 | from django.db import migrations, models 4 | import django.db.models.deletion 5 | 6 | 7 | class Migration(migrations.Migration): 8 | 9 | dependencies = [ 10 | ("amazon_backend_api", "0007_size"), 11 | ] 12 | 13 | operations = [ 14 | migrations.CreateModel( 15 | name="Product", 16 | fields=[ 17 | ( 18 | "id", 19 | models.BigAutoField( 20 | auto_created=True, 21 | primary_key=True, 22 | serialize=False, 23 | verbose_name="ID", 24 | ), 25 | ), 26 | ("created_at", models.DateTimeField(auto_now_add=True, null=True)), 27 | ("updated_at", models.DateTimeField(auto_now=True, null=True)), 28 | ("name", models.CharField(max_length=200, verbose_name="Product Name")), 29 | ( 30 | "brand", 31 | models.ForeignKey( 32 | null=True, 33 | on_delete=django.db.models.deletion.SET_NULL, 34 | related_name="Brand_Name", 35 | to="amazon_backend_api.brand", 36 | ), 37 | ), 38 | ( 39 | "category", 40 | models.ForeignKey( 41 | null=True, 42 | on_delete=django.db.models.deletion.SET_NULL, 43 | related_name="Category", 44 | to="amazon_backend_api.category", 45 | ), 46 | ), 47 | ( 48 | "subcategory1", 49 | models.ForeignKey( 50 | null=True, 51 | on_delete=django.db.models.deletion.SET_NULL, 52 | related_name="SubCategory1", 53 | to="amazon_backend_api.subcategory", 54 | ), 55 | ), 56 | ( 57 | "subcategory2", 58 | models.ForeignKey( 59 | null=True, 60 | on_delete=django.db.models.deletion.SET_NULL, 61 | related_name="SubCategory2", 62 | to="amazon_backend_api.subcategory", 63 | ), 64 | ), 65 | ], 66 | options={"abstract": False,}, 67 | ), 68 | ] 69 | -------------------------------------------------------------------------------- /amazon_backend_api/migrations/0009_productdetail.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 4.1.5 on 2023-02-01 13:15 2 | 3 | from django.db import migrations, models 4 | import django.db.models.deletion 5 | 6 | 7 | class Migration(migrations.Migration): 8 | 9 | dependencies = [ 10 | ("amazon_backend_api", "0008_product"), 11 | ] 12 | 13 | operations = [ 14 | migrations.CreateModel( 15 | name="ProductDetail", 16 | fields=[ 17 | ( 18 | "id", 19 | models.BigAutoField( 20 | auto_created=True, 21 | primary_key=True, 22 | serialize=False, 23 | verbose_name="ID", 24 | ), 25 | ), 26 | ("created_at", models.DateTimeField(auto_now_add=True, null=True)), 27 | ("updated_at", models.DateTimeField(auto_now=True, null=True)), 28 | ( 29 | "description", 30 | models.TextField( 31 | max_length=1000, verbose_name="Product Descriptions" 32 | ), 33 | ), 34 | ( 35 | "mrp", 36 | models.DecimalField( 37 | decimal_places=2, max_digits=10, verbose_name="MRP" 38 | ), 39 | ), 40 | ( 41 | "discount", 42 | models.DecimalField( 43 | decimal_places=2, max_digits=10, verbose_name="Discount" 44 | ), 45 | ), 46 | ( 47 | "image1", 48 | models.ImageField(upload_to="products", verbose_name="Image 1"), 49 | ), 50 | ( 51 | "image2", 52 | models.ImageField(upload_to="products", verbose_name="Image 2"), 53 | ), 54 | ( 55 | "image3", 56 | models.ImageField(upload_to="products", verbose_name="Image 3"), 57 | ), 58 | ( 59 | "product", 60 | models.ForeignKey( 61 | on_delete=django.db.models.deletion.CASCADE, 62 | related_name="product", 63 | to="amazon_backend_api.product", 64 | ), 65 | ), 66 | ( 67 | "size", 68 | models.ManyToManyField( 69 | related_name="size", to="amazon_backend_api.size" 70 | ), 71 | ), 72 | ], 73 | options={"abstract": False,}, 74 | ), 75 | ] 76 | -------------------------------------------------------------------------------- /amazon_backend_api/migrations/0010_color.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 4.1.5 on 2023-02-01 13:26 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ("amazon_backend_api", "0009_productdetail"), 10 | ] 11 | 12 | operations = [ 13 | migrations.CreateModel( 14 | name="Color", 15 | fields=[ 16 | ( 17 | "id", 18 | models.BigAutoField( 19 | auto_created=True, 20 | primary_key=True, 21 | serialize=False, 22 | verbose_name="ID", 23 | ), 24 | ), 25 | ("created_at", models.DateTimeField(auto_now_add=True, null=True)), 26 | ("updated_at", models.DateTimeField(auto_now=True, null=True)), 27 | ("name", models.CharField(max_length=100, verbose_name="Color Name")), 28 | ("code", models.CharField(max_length=100, verbose_name="Color Code")), 29 | ], 30 | options={"abstract": False,}, 31 | ), 32 | ] 33 | -------------------------------------------------------------------------------- /amazon_backend_api/migrations/0011_productdetail_color.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 4.1.5 on 2023-02-01 13:28 2 | 3 | from django.db import migrations, models 4 | import django.db.models.deletion 5 | 6 | 7 | class Migration(migrations.Migration): 8 | 9 | dependencies = [ 10 | ("amazon_backend_api", "0010_color"), 11 | ] 12 | 13 | operations = [ 14 | migrations.AddField( 15 | model_name="productdetail", 16 | name="color", 17 | field=models.ForeignKey( 18 | null=True, 19 | on_delete=django.db.models.deletion.SET_NULL, 20 | related_name="Color", 21 | to="amazon_backend_api.color", 22 | ), 23 | ), 24 | ] 25 | -------------------------------------------------------------------------------- /amazon_backend_api/migrations/0012_productdetail_is_stock_productdetail_stocks.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 4.1.5 on 2023-02-02 06:08 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ("amazon_backend_api", "0011_productdetail_color"), 10 | ] 11 | 12 | operations = [ 13 | migrations.AddField( 14 | model_name="productdetail", 15 | name="is_stock", 16 | field=models.BooleanField(default=True, verbose_name="Stocks Avaliable"), 17 | ), 18 | migrations.AddField( 19 | model_name="productdetail", 20 | name="stocks", 21 | field=models.IntegerField(default=100, verbose_name="Number of Stocks"), 22 | preserve_default=False, 23 | ), 24 | ] 25 | -------------------------------------------------------------------------------- /amazon_backend_api/migrations/0013_cart.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 4.1.5 on 2023-02-02 10:44 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 | dependencies = [ 11 | ("amazon_backend_api", "0012_productdetail_is_stock_productdetail_stocks"), 12 | ] 13 | 14 | operations = [ 15 | migrations.CreateModel( 16 | name="Cart", 17 | fields=[ 18 | ( 19 | "id", 20 | models.BigAutoField( 21 | auto_created=True, 22 | primary_key=True, 23 | serialize=False, 24 | verbose_name="ID", 25 | ), 26 | ), 27 | ("created_at", models.DateTimeField(auto_now_add=True, null=True)), 28 | ("updated_at", models.DateTimeField(auto_now=True, null=True)), 29 | ("quantity", models.IntegerField(default=1, verbose_name="Quantity")), 30 | ( 31 | "product", 32 | models.ForeignKey( 33 | on_delete=django.db.models.deletion.CASCADE, 34 | related_name="Product", 35 | to="amazon_backend_api.productdetail", 36 | ), 37 | ), 38 | ( 39 | "user", 40 | models.ForeignKey( 41 | on_delete=django.db.models.deletion.CASCADE, 42 | related_name="AmazonUser", 43 | to=settings.AUTH_USER_MODEL, 44 | ), 45 | ), 46 | ], 47 | options={"abstract": False,}, 48 | ), 49 | ] 50 | -------------------------------------------------------------------------------- /amazon_backend_api/migrations/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Aashishkumar123/amazon-backend/da4baab5479746141dd66cc4741813d52fed34c8/amazon_backend_api/migrations/__init__.py -------------------------------------------------------------------------------- /amazon_backend_api/models.py: -------------------------------------------------------------------------------- 1 | from django.db import models 2 | from amazon_backend_api.manager import AmazonuserManager 3 | from django.contrib.auth.models import AbstractUser 4 | from django.utils.translation import gettext_lazy as _ 5 | from amazon_backend_api.api.utils import INDIAN_STATES 6 | 7 | 8 | STATE_CHOICES = ((state, INDIAN_STATES.get(state)) for state in INDIAN_STATES) 9 | 10 | 11 | class Amazonuser(AbstractUser): 12 | username = None 13 | first_name = None 14 | last_name = None 15 | email = models.EmailField(_("email address"), unique=True) 16 | full_name = models.CharField(_("full name"), max_length=100) 17 | 18 | USERNAME_FIELD = "email" 19 | REQUIRED_FIELDS = [] 20 | 21 | objects = AmazonuserManager() 22 | 23 | def __str__(self): 24 | return self.email 25 | 26 | 27 | class BaseModel(models.Model): 28 | 29 | created_at = models.DateTimeField(auto_now_add=True, null=True) 30 | updated_at = models.DateTimeField(auto_now=True, null=True) 31 | 32 | class Meta: 33 | abstract = True 34 | 35 | 36 | class UserAddress(BaseModel): 37 | 38 | user = models.ForeignKey(Amazonuser, on_delete=models.CASCADE, verbose_name="User") 39 | country = models.CharField( 40 | verbose_name="Country", choices=(("India", "India"),), max_length=100 41 | ) 42 | full_name = models.CharField( 43 | verbose_name="Full name (First and last name)", max_length=100 44 | ) 45 | mobile_number = models.IntegerField(verbose_name="Mobile number") 46 | pincode = models.IntegerField(verbose_name="Pincode") 47 | flat = models.CharField( 48 | verbose_name="Flat, House no., Building, Company, Apartment", max_length=200 49 | ) 50 | street = models.CharField( 51 | verbose_name="Area, Street, Sector, Village", max_length=200 52 | ) 53 | landmark = models.CharField(verbose_name="Landmark", max_length=100) 54 | town = models.CharField(verbose_name="Town/City", max_length=100) 55 | state = models.CharField( 56 | verbose_name="State", choices=STATE_CHOICES, max_length=100 57 | ) 58 | default = models.BooleanField( 59 | verbose_name="Make this my default address", default=False 60 | ) 61 | address_type = models.CharField( 62 | verbose_name="Address Type", 63 | max_length=100, 64 | choices=(("Home", "Home"), ("Office", "Office")), 65 | ) 66 | 67 | def __str__(self): 68 | return str(f"{self.full_name} Address") 69 | 70 | 71 | class Brand(BaseModel): 72 | name = models.CharField(verbose_name="Brand Name", max_length=100) 73 | logo = models.ImageField(upload_to="brands/logo") 74 | 75 | def __str__(self): 76 | return str(f"{self.name}") 77 | 78 | 79 | class Category(BaseModel): 80 | name = models.CharField(verbose_name="Category Name", max_length=100) 81 | 82 | def __str__(self): 83 | return str(f"{self.name}") 84 | 85 | 86 | class Subcategory(BaseModel): 87 | name = models.CharField(verbose_name="SubCategory Name", max_length=100) 88 | 89 | def __str__(self): 90 | return str(f"{self.name}") 91 | 92 | 93 | class Size(BaseModel): 94 | name = models.CharField(verbose_name="Size", max_length=100) 95 | 96 | def __str__(self): 97 | return str(f"{self.name}") 98 | 99 | 100 | class Color(BaseModel): 101 | name = models.CharField(verbose_name="Color Name", max_length=100) 102 | code = models.CharField(verbose_name="Color Code", max_length=100) 103 | 104 | def __str__(self): 105 | return str(f"{self.name}") 106 | 107 | 108 | class Product(BaseModel): 109 | name = models.CharField(verbose_name="Product Name", max_length=200) 110 | brand = models.ForeignKey( 111 | Brand, on_delete=models.SET_NULL, related_name="Brand_Name", null=True 112 | ) 113 | category = models.ForeignKey( 114 | Category, on_delete=models.SET_NULL, related_name="Category", null=True 115 | ) 116 | subcategory1 = models.ForeignKey( 117 | Subcategory, on_delete=models.SET_NULL, related_name="SubCategory1", null=True 118 | ) 119 | subcategory2 = models.ForeignKey( 120 | Subcategory, on_delete=models.SET_NULL, related_name="SubCategory2", null=True 121 | ) 122 | 123 | def __str__(self) -> str: 124 | return str(f"{self.name}") 125 | 126 | 127 | class ProductDetail(BaseModel): 128 | product = models.ForeignKey( 129 | Product, on_delete=models.CASCADE, related_name="product" 130 | ) 131 | size = models.ManyToManyField(Size, related_name="size") 132 | color = models.ForeignKey( 133 | Color, on_delete=models.SET_NULL, null=True, related_name="Color" 134 | ) 135 | description = models.TextField(verbose_name="Product Descriptions", max_length=1000) 136 | mrp = models.DecimalField(verbose_name="MRP", decimal_places=2, max_digits=10) 137 | discount = models.DecimalField( 138 | verbose_name="Discount", decimal_places=2, max_digits=10 139 | ) 140 | stocks = models.IntegerField(verbose_name="Number of Stocks") 141 | is_stock = models.BooleanField(verbose_name="Stocks Avaliable", default=True) 142 | image1 = models.ImageField(upload_to="products", verbose_name="Image 1") 143 | image2 = models.ImageField(upload_to="products", verbose_name="Image 2") 144 | image3 = models.ImageField(upload_to="products", verbose_name="Image 3") 145 | 146 | def __str__(self) -> str: 147 | return str(f"{self.product} Details") 148 | 149 | 150 | class Cart(BaseModel): 151 | user = models.ForeignKey( 152 | Amazonuser, on_delete=models.CASCADE, related_name="AmazonUser" 153 | ) 154 | product = models.ForeignKey( 155 | ProductDetail, on_delete=models.CASCADE, related_name="Product" 156 | ) 157 | quantity = models.IntegerField(verbose_name="Quantity", default=1) 158 | 159 | def __str__(self): 160 | return str(f"{self.product}") 161 | -------------------------------------------------------------------------------- /amazon_backend_api/tests.py: -------------------------------------------------------------------------------- 1 | from django.urls import reverse 2 | from rest_framework import status 3 | from rest_framework.test import APITestCase 4 | from amazon_backend_api.models import Amazonuser 5 | from django.contrib.auth.hashers import make_password 6 | 7 | 8 | class TestRegistration(APITestCase): 9 | """ 10 | This will handle Registration Test Case 11 | """ 12 | 13 | def setUp(self): 14 | self.url = reverse("amz-user-register") 15 | 16 | def test_registration(self): 17 | """ 18 | This will test registration 19 | """ 20 | 21 | data = { 22 | "full_name": "Aashish Kumar", 23 | "email": "wek@gmail.com", 24 | "password": "bwfjjwje", 25 | } 26 | 27 | response = self.client.post(self.url, data=data) 28 | 29 | self.assertEqual( 30 | response.status_code, status.HTTP_201_CREATED, msg="test_registration" 31 | ) 32 | 33 | def test_without_data_registration(self): 34 | """ 35 | This will test registration without data 36 | """ 37 | 38 | response = self.client.post(self.url) 39 | self.assertEqual( 40 | response.status_code, 41 | status.HTTP_400_BAD_REQUEST, 42 | msg="test_without_data_registration_statuscode", 43 | ) 44 | self.assertEqual( 45 | response.data.get("data").get("full_name")[0], 46 | "This field is required.", 47 | msg="test_without_fullname_registration_data_fullname", 48 | ) 49 | self.assertEqual( 50 | response.data.get("data").get("email")[0], 51 | "This field is required.", 52 | msg="test_without_fullname_registration_data_email", 53 | ) 54 | self.assertEqual( 55 | response.data.get("data").get("password")[0], 56 | "This field is required.", 57 | msg="test_without_fullname_registration_data_password", 58 | ) 59 | 60 | def test_without_fullname_registration(self): 61 | """ 62 | This will test registration without fullname 63 | """ 64 | 65 | data = {"email": "wek@gmail.com", "password": "bwfjjwje"} 66 | response = self.client.post(self.url, data=data) 67 | self.assertEqual( 68 | response.status_code, 69 | status.HTTP_400_BAD_REQUEST, 70 | msg="test_without_fullname_registration_statuscode", 71 | ) 72 | self.assertEqual( 73 | response.data.get("data").get("full_name")[0], 74 | "This field is required.", 75 | msg="test_without_fullname_registration_data", 76 | ) 77 | 78 | def test_without_email_registration(self): 79 | """ 80 | This will test registration without email 81 | """ 82 | 83 | data = {"full_name": "Aashish Kumar", "password": "bwfjjwje"} 84 | response = self.client.post(self.url, data=data) 85 | self.assertEqual( 86 | response.status_code, 87 | status.HTTP_400_BAD_REQUEST, 88 | msg="test_without_email_registration_statuscode", 89 | ) 90 | self.assertEqual( 91 | response.data.get("data").get("email")[0], 92 | "This field is required.", 93 | msg="test_without_email_registration_data", 94 | ) 95 | 96 | def test_without_password_registration(self): 97 | """ 98 | This will test registration without password 99 | """ 100 | 101 | data = { 102 | "full_name": "Aashish Kumar", 103 | "email": "wek@gmail.com", 104 | } 105 | response = self.client.post(self.url, data=data) 106 | self.assertEqual( 107 | response.status_code, 108 | status.HTTP_400_BAD_REQUEST, 109 | msg="test_without_password_registration_statuscode", 110 | ) 111 | self.assertEqual( 112 | response.data.get("data").get("password")[0], 113 | "This field is required.", 114 | msg="test_without_password_registration_data", 115 | ) 116 | 117 | def test_invalid_email_registration(self): 118 | """ 119 | This will test invalid email address for registration... 120 | """ 121 | 122 | data = { 123 | "full_name": "Aashish Kumar", 124 | "email": "wek@gmail.", 125 | "password": "bwfjjwje", 126 | } 127 | 128 | response = self.client.post(self.url, data=data) 129 | 130 | self.assertEqual( 131 | response.status_code, 132 | status.HTTP_400_BAD_REQUEST, 133 | msg="test_invalid_email_registration_statuscode", 134 | ) 135 | self.assertEqual( 136 | response.data.get("data").get("email")[0], 137 | "Enter a valid email address.", 138 | msg="test_invalid_emai_registration_data", 139 | ) 140 | 141 | def test_exists_email_registration(self): 142 | """ 143 | This will test exist email address for registration... 144 | """ 145 | 146 | data = { 147 | "full_name": "Aashish Kumar", 148 | "email": "whghg@gmail.com", 149 | "password": "bwfjjwje", 150 | } 151 | 152 | Amazonuser.objects.create( 153 | full_name=data.get("full_name"), 154 | email=data.get("email"), 155 | password=data.get("password"), 156 | ) 157 | response = self.client.post(self.url, data=data) 158 | 159 | self.assertEqual( 160 | response.status_code, 161 | status.HTTP_400_BAD_REQUEST, 162 | msg="test_exists_email_registration_statuscode", 163 | ) 164 | self.assertEqual( 165 | response.data.get("data").get("email")[0], 166 | "user with this email address already exists.", 167 | msg="test_exists_email_registration_data", 168 | ) 169 | 170 | 171 | class TestLogin(APITestCase): 172 | """ 173 | This will handle login testcases 174 | """ 175 | 176 | def setUp(self): 177 | self.url = reverse("amz-user-sign-in") 178 | 179 | def test_login(self): 180 | """ 181 | This will test successfull login 182 | """ 183 | data = { 184 | "full_name": "Aashish Kumar", 185 | "email": "whghg@gmail.com", 186 | "password": "bwfjjwje", 187 | } 188 | 189 | Amazonuser.objects.create( 190 | full_name=data.get("full_name"), 191 | email=data.get("email"), 192 | password=make_password(data.get("password")), 193 | ) 194 | 195 | response = self.client.post(self.url, data=data) 196 | self.assertEqual( 197 | response.status_code, 198 | status.HTTP_200_OK, 199 | msg="test_login_successfull_statuscode", 200 | ) 201 | 202 | def test_login_without_data(self): 203 | """ 204 | This will test login without data from client 205 | """ 206 | 207 | response = self.client.post(self.url) 208 | self.assertEqual( 209 | response.status_code, 210 | status.HTTP_400_BAD_REQUEST, 211 | msg="test_login_without_data_statuscode", 212 | ) 213 | 214 | self.assertEqual( 215 | response.data.get("data").get("email")[0], 216 | "This field is required.", 217 | msg="test_login_without_data_DATA_email", 218 | ) 219 | 220 | self.assertEqual( 221 | response.data.get("data").get("password")[0], 222 | "This field is required.", 223 | msg="test_login_without_data_DATA_password", 224 | ) 225 | 226 | def test_login_with_invalid_credentials(self): 227 | """ 228 | This will test login with invalid credentials 229 | """ 230 | 231 | """wrong credentials""" 232 | data = {"email": "whghg@gmail.com", "password": "bwfjjwje"} 233 | 234 | response = self.client.post(self.url, data=data) 235 | self.assertEqual( 236 | response.status_code, 237 | status.HTTP_401_UNAUTHORIZED, 238 | msg="test_login_with_invalid_credentials_statuscode", 239 | ) 240 | self.assertEqual( 241 | response.data.get("message"), 242 | "Invalid Email or Password", 243 | msg="test_login_with_invalid_credentials_message", 244 | ) 245 | 246 | def test_login_with_empty_data(self): 247 | """ 248 | This will test login with empty data from client 249 | """ 250 | 251 | """empty data""" 252 | data = {"email": "", "password": ""} 253 | 254 | response = self.client.post(self.url, data=data) 255 | self.assertEqual( 256 | response.status_code, 257 | status.HTTP_400_BAD_REQUEST, 258 | msg="test_login_with_empty_data_statuscode", 259 | ) 260 | 261 | self.assertEqual( 262 | response.data.get("data").get("email")[0], 263 | "This field may not be blank.", 264 | msg="test_login_with_empty_data_DATA_email", 265 | ) 266 | 267 | self.assertEqual( 268 | response.data.get("data").get("password")[0], 269 | "This field may not be blank.", 270 | msg="test_login_with_empty_data_DATA_password", 271 | ) 272 | -------------------------------------------------------------------------------- /amazon_backend_api/views.py: -------------------------------------------------------------------------------- 1 | from django.shortcuts import render 2 | 3 | # Create your views here. 4 | -------------------------------------------------------------------------------- /amz-media/brands/logo/download.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Aashishkumar123/amazon-backend/da4baab5479746141dd66cc4741813d52fed34c8/amz-media/brands/logo/download.png -------------------------------------------------------------------------------- /amz-media/brands/logo/hrx.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Aashishkumar123/amazon-backend/da4baab5479746141dd66cc4741813d52fed34c8/amz-media/brands/logo/hrx.png -------------------------------------------------------------------------------- /amz-media/products/519I05HaHjL._SX569._SX._UX._SY._UY_.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Aashishkumar123/amazon-backend/da4baab5479746141dd66cc4741813d52fed34c8/amz-media/products/519I05HaHjL._SX569._SX._UX._SY._UY_.jpg -------------------------------------------------------------------------------- /amz-media/products/51raYlT-AUL._UX569_.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Aashishkumar123/amazon-backend/da4baab5479746141dd66cc4741813d52fed34c8/amz-media/products/51raYlT-AUL._UX569_.jpg -------------------------------------------------------------------------------- /amz-media/products/61UagAR6IcL._UX569_.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Aashishkumar123/amazon-backend/da4baab5479746141dd66cc4741813d52fed34c8/amz-media/products/61UagAR6IcL._UX569_.jpg -------------------------------------------------------------------------------- /amz-media/products/61UagAR6IcL._UX569__xI8CtqU.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Aashishkumar123/amazon-backend/da4baab5479746141dd66cc4741813d52fed34c8/amz-media/products/61UagAR6IcL._UX569__xI8CtqU.jpg -------------------------------------------------------------------------------- /amz-media/products/61mUSVo0b7S._SX569._SX._UX._SY._UY_.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Aashishkumar123/amazon-backend/da4baab5479746141dd66cc4741813d52fed34c8/amz-media/products/61mUSVo0b7S._SX569._SX._UX._SY._UY_.jpg -------------------------------------------------------------------------------- /amz-media/products/71mBMP1WTsL._SX569._SX._UX._SY._UY_.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Aashishkumar123/amazon-backend/da4baab5479746141dd66cc4741813d52fed34c8/amz-media/products/71mBMP1WTsL._SX569._SX._UX._SY._UY_.jpg -------------------------------------------------------------------------------- /backend/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Aashishkumar123/amazon-backend/da4baab5479746141dd66cc4741813d52fed34c8/backend/__init__.py -------------------------------------------------------------------------------- /backend/asgi.py: -------------------------------------------------------------------------------- 1 | """ 2 | ASGI config for backend 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.2/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', 'backend.settings') 15 | 16 | application = get_asgi_application() 17 | -------------------------------------------------------------------------------- /backend/settings.py: -------------------------------------------------------------------------------- 1 | """ 2 | Django settings for backend project. 3 | 4 | Generated by 'django-admin startproject' using Django 3.2. 5 | 6 | For more information on this file, see 7 | https://docs.djangoproject.com/en/3.2/topics/settings/ 8 | 9 | For the full list of settings and their values, see 10 | https://docs.djangoproject.com/en/3.2/ref/settings/ 11 | """ 12 | 13 | from pathlib import Path 14 | from datetime import timedelta 15 | 16 | # Build paths inside the project like this: BASE_DIR / 'subdir'. 17 | BASE_DIR = Path(__file__).resolve().parent.parent 18 | 19 | 20 | # Quick-start development settings - unsuitable for production 21 | # See https://docs.djangoproject.com/en/3.2/howto/deployment/checklist/ 22 | 23 | # SECURITY WARNING: keep the secret key used in production secret! 24 | SECRET_KEY = 'django-insecure-a104wyjj=x2i71z6$b7f1aq(xx$nd1^2h5ou&r^3h$bl*zkb5g' 25 | 26 | # SECURITY WARNING: don't run with debug turned on in production! 27 | DEBUG = True 28 | 29 | ALLOWED_HOSTS = [] 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 | 'rest_framework', 42 | 'rest_framework_simplejwt', 43 | 'drf_yasg', 44 | 'corsheaders', 45 | 'amazon_backend_api.apps.AmazonBackendApiConfig', 46 | ] 47 | 48 | MIDDLEWARE = [ 49 | 'django.middleware.security.SecurityMiddleware', 50 | 'django.contrib.sessions.middleware.SessionMiddleware', 51 | 'corsheaders.middleware.CorsMiddleware', 52 | 'django.middleware.common.CommonMiddleware', 53 | 'django.middleware.csrf.CsrfViewMiddleware', 54 | 'django.contrib.auth.middleware.AuthenticationMiddleware', 55 | 'django.contrib.messages.middleware.MessageMiddleware', 56 | 'django.middleware.clickjacking.XFrameOptionsMiddleware', 57 | ] 58 | 59 | ROOT_URLCONF = 'backend.urls' 60 | 61 | TEMPLATES = [ 62 | { 63 | 'BACKEND': 'django.template.backends.django.DjangoTemplates', 64 | 'DIRS': [], 65 | 'APP_DIRS': True, 66 | 'OPTIONS': { 67 | 'context_processors': [ 68 | 'django.template.context_processors.debug', 69 | 'django.template.context_processors.request', 70 | 'django.contrib.auth.context_processors.auth', 71 | 'django.contrib.messages.context_processors.messages', 72 | ], 73 | }, 74 | }, 75 | ] 76 | 77 | WSGI_APPLICATION = 'backend.wsgi.application' 78 | 79 | 80 | # Database 81 | # https://docs.djangoproject.com/en/3.2/ref/settings/#databases 82 | 83 | DATABASES = { 84 | 'default': { 85 | 'ENGINE': 'django.db.backends.postgresql', 86 | 'NAME': 'amazon-backend-db', 87 | 'USER': 'postgres', 88 | 'PASSWORD': 'aakumar123', 89 | 'HOST': 'localhost', 90 | 'PORT': '5432', 91 | } 92 | } 93 | 94 | 95 | # Password validation 96 | # https://docs.djangoproject.com/en/3.2/ref/settings/#auth-password-validators 97 | 98 | AUTH_PASSWORD_VALIDATORS = [ 99 | { 100 | 'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator', 101 | }, 102 | { 103 | 'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator', 104 | }, 105 | { 106 | 'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator', 107 | }, 108 | { 109 | 'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator', 110 | }, 111 | ] 112 | 113 | 114 | # Internationalization 115 | # https://docs.djangoproject.com/en/3.2/topics/i18n/ 116 | 117 | LANGUAGE_CODE = 'en-us' 118 | 119 | TIME_ZONE = 'UTC' 120 | 121 | USE_I18N = True 122 | 123 | USE_L10N = True 124 | 125 | USE_TZ = True 126 | 127 | 128 | # Static files (CSS, JavaScript, Images) 129 | # https://docs.djangoproject.com/en/3.2/howto/static-files/ 130 | 131 | STATIC_URL = '/static/' 132 | 133 | # Default primary key field type 134 | # https://docs.djangoproject.com/en/3.2/ref/settings/#default-auto-field 135 | 136 | DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField' 137 | 138 | 139 | #REGISTER CUSTOM USER 140 | AUTH_USER_MODEL = 'amazon_backend_api.Amazonuser' 141 | 142 | 143 | #REST FRAMEWORK SETTINGS 144 | REST_FRAMEWORK = { 145 | 'DEFAULT_AUTHENTICATION_CLASSES': ( 146 | 'rest_framework_simplejwt.authentication.JWTAuthentication', 147 | ) 148 | } 149 | 150 | #SIMPLE JWT SETTINGS 151 | SIMPLE_JWT = { 152 | 'ACCESS_TOKEN_LIFETIME': timedelta(minutes=60), 153 | 'REFRESH_TOKEN_LIFETIME': timedelta(days=365), 154 | 'ROTATE_REFRESH_TOKENS': False, 155 | 'BLACKLIST_AFTER_ROTATION': False, 156 | 'UPDATE_LAST_LOGIN': False, 157 | 158 | 'ALGORITHM': 'HS256', 159 | 'SIGNING_KEY': SECRET_KEY, 160 | 'VERIFYING_KEY': None, 161 | 'AUDIENCE': None, 162 | 'ISSUER': None, 163 | 'JWK_URL': None, 164 | 'LEEWAY': 0, 165 | 166 | 'AUTH_HEADER_TYPES': ('Bearer',), 167 | 'AUTH_HEADER_NAME': 'HTTP_AUTHORIZATION', 168 | 'USER_ID_FIELD': 'email', 169 | 'USER_ID_CLAIM': 'email', 170 | 'USER_AUTHENTICATION_RULE': 'rest_framework_simplejwt.authentication.default_user_authentication_rule', 171 | 172 | 'AUTH_TOKEN_CLASSES': ('rest_framework_simplejwt.tokens.AccessToken',), 173 | 'TOKEN_TYPE_CLAIM': 'token_type', 174 | 'TOKEN_USER_CLASS': 'rest_framework_simplejwt.models.TokenUser', 175 | 176 | 'JTI_CLAIM': 'jti', 177 | 178 | 'SLIDING_TOKEN_REFRESH_EXP_CLAIM': 'refresh_exp', 179 | 'SLIDING_TOKEN_LIFETIME': timedelta(minutes=5), 180 | 'SLIDING_TOKEN_REFRESH_LIFETIME': timedelta(days=1), 181 | } 182 | 183 | #MEDIA SETTINGS 184 | MEDIA_URL = '/amz-media/' 185 | MEDIA_ROOT = BASE_DIR/'amz-media' 186 | 187 | #CORS HEADERS SETTINGS 188 | CORS_ALLOWED_ORIGINS = [ 189 | 'http://localhost:3000' 190 | ] 191 | -------------------------------------------------------------------------------- /backend/urls.py: -------------------------------------------------------------------------------- 1 | """backend URL Configuration 2 | 3 | The `urlpatterns` list routes URLs to views. For more information please see: 4 | https://docs.djangoproject.com/en/3.2/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 | from django.conf import settings 19 | from django.conf.urls.static import static 20 | 21 | 22 | urlpatterns = [ 23 | path("admin/", admin.site.urls), 24 | path("api/amz/", include("amazon_backend_api.api.urls")), 25 | ] + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT) 26 | -------------------------------------------------------------------------------- /backend/wsgi.py: -------------------------------------------------------------------------------- 1 | """ 2 | WSGI config for backend 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.2/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', 'backend.settings') 15 | 16 | application = get_wsgi_application() 17 | -------------------------------------------------------------------------------- /docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: '3.9' 2 | 3 | services: 4 | django: 5 | image: amazon-api:0.0.1 6 | build: . 7 | ports: 8 | - "8000:8000" 9 | -------------------------------------------------------------------------------- /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 | """Run administrative tasks.""" 9 | os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'backend.settings') 10 | try: 11 | from django.core.management import execute_from_command_line 12 | except ImportError as exc: 13 | raise ImportError( 14 | "Couldn't import Django. Are you sure it's installed and " 15 | "available on your PYTHONPATH environment variable? Did you " 16 | "forget to activate a virtual environment?" 17 | ) from exc 18 | execute_from_command_line(sys.argv) 19 | 20 | 21 | if __name__ == '__main__': 22 | main() 23 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | asgiref==3.6.0 2 | black==23.1.0 3 | certifi==2022.12.7 4 | charset-normalizer==3.0.1 5 | click==8.1.3 6 | coreapi==2.3.3 7 | coreschema==0.0.4 8 | Django==4.1.5 9 | django-cors-headers==3.13.0 10 | djangorestframework==3.14.0 11 | djangorestframework-simplejwt==5.2.2 12 | drf-yasg==1.21.4 13 | flake8==6.0.0 14 | idna==3.4 15 | inflection==0.5.1 16 | itypes==1.2.0 17 | Jinja2==3.1.2 18 | MarkupSafe==2.1.2 19 | mccabe==0.7.0 20 | mypy-extensions==1.0.0 21 | packaging==23.0 22 | pathspec==0.11.0 23 | Pillow==9.4.0 24 | platformdirs==3.0.0 25 | psycopg2-binary==2.9.5 26 | pycodestyle==2.10.0 27 | pyflakes==3.0.1 28 | PyJWT==2.6.0 29 | pytz==2022.7.1 30 | requests==2.28.2 31 | ruamel.yaml==0.17.21 32 | ruamel.yaml.clib==0.2.7 33 | sqlparse==0.4.3 34 | tomli==2.0.1 35 | typing_extensions==4.4.0 36 | uritemplate==4.1.1 37 | urllib3==1.26.14 38 | --------------------------------------------------------------------------------