├── .gitignore
├── .idea
├── .name
├── djangodm.iml
├── inspectionProfiles
│ ├── Project_Default.xml
│ └── profiles_settings.xml
├── misc.xml
├── modules.xml
├── vcs.xml
└── workspace.xml
├── README.md
├── analytics
├── __init__.py
├── admin.py
├── migrations
│ ├── 0001_initial.py
│ └── __init__.py
├── models.py
├── tests.py
└── views.py
├── billing
├── __init__.py
├── admin.py
├── migrations
│ ├── 0001_initial.py
│ └── __init__.py
├── models.py
├── tests.py
└── views.py
├── checkout
├── __init__.py
├── admin.py
├── migrations
│ └── __init__.py
├── models.py
├── tests.py
└── views.py
├── dashboard
├── __init__.py
├── admin.py
├── migrations
│ └── __init__.py
├── models.py
├── tests.py
└── views.py
├── djangodm
├── __init__.py
├── decorators.py
├── mixins.py
├── settings
│ ├── __init__.py
│ ├── base.py
│ ├── dev.py
│ └── production.py
├── urls.py
└── wsgi.py
├── manage.py
├── products
├── __init__.py
├── admin.py
├── forms.py
├── migrations
│ ├── 0001_initial.py
│ ├── 0002_auto_20151225_1715.py
│ ├── 0003_auto_20151225_1725.py
│ ├── 0004_auto_20151226_0250.py
│ ├── 0005_product_user.py
│ ├── 0006_product_manager.py
│ ├── 0007_auto_20151226_0646.py
│ ├── 0008_product_media.py
│ ├── 0009_auto_20151227_0602.py
│ ├── 0010_myproducts.py
│ ├── 0011_auto_20151231_0536.py
│ ├── 0012_auto_20151231_0600.py
│ ├── 0013_auto_20151231_0634.py
│ ├── 0014_remove_thumbnail_user.py
│ ├── 0015_product_sale_active.py
│ ├── 0016_auto_20160224_1315.py
│ ├── 0017_auto_20160318_2334.py
│ ├── 0018_curatedproducts.py
│ └── __init__.py
├── mixins.py
├── models.py
├── templatetags
│ ├── __init__.py
│ └── get_thumbnail.py
├── tests.py
├── urls.py
└── views.py
├── requirement.txt
├── sellers
├── __init__.py
├── admin.py
├── forms.py
├── migrations
│ ├── 0001_initial.py
│ └── __init__.py
├── mixins.py
├── models.py
├── tests.py
├── urls.py
└── views.py
├── tags
├── __init__.py
├── admin.py
├── migrations
│ ├── 0001_initial.py
│ └── __init__.py
├── models.py
├── tests.py
├── urls.py
└── views.py
└── templates
├── base.html
├── checkout
└── test.html
├── create_view.html
├── dashboard
└── view.html
├── detail_view.html
├── form.html
├── form_include.html
├── list_view.html
├── products
├── library_list.html
├── product_detail.html
├── product_list.html
└── product_list_snippet.html
├── sellers
├── dashboard.html
├── product_list_view.html
├── transaction_list.html
└── transaction_list_view.html
├── tags
├── tag_detail.html
└── tag_list.html
└── update_view.html
/.gitignore:
--------------------------------------------------------------------------------
1 | db.sqlite3
2 | .DS_STORE
3 | .Python
4 | *.pyc
5 | *.pyo
6 | /static
7 | /media
8 | /static_cdn
9 |
--------------------------------------------------------------------------------
/.idea/.name:
--------------------------------------------------------------------------------
1 | djangodm
--------------------------------------------------------------------------------
/.idea/djangodm.iml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
25 |
26 |
--------------------------------------------------------------------------------
/.idea/inspectionProfiles/Project_Default.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
--------------------------------------------------------------------------------
/.idea/inspectionProfiles/profiles_settings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
--------------------------------------------------------------------------------
/.idea/misc.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/.idea/modules.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/.idea/vcs.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/.idea/workspace.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 | true
110 |
111 |
112 |
113 |
114 |
115 |
116 |
117 |
118 |
119 |
120 |
121 |
122 |
123 |
124 |
125 |
126 |
127 |
128 |
129 |
130 |
131 |
132 |
133 |
134 |
135 |
136 |
137 |
138 |
139 |
140 |
141 |
142 |
143 |
144 |
145 |
146 |
147 |
148 |
149 |
150 |
151 |
152 |
153 |
154 |
155 |
156 |
157 |
158 |
159 |
160 |
161 |
162 |
163 |
164 |
165 |
166 |
167 |
168 |
169 |
170 |
171 |
172 |
173 |
174 |
175 |
176 |
177 |
178 |
179 |
180 |
181 |
182 |
183 |
184 |
185 |
186 |
187 |
188 |
189 |
190 |
191 |
192 |
193 |
194 |
195 |
196 |
197 |
198 |
199 |
200 |
201 |
202 |
203 |
204 |
205 |
206 |
207 |
208 |
209 |
210 |
211 |
212 |
213 |
214 |
215 |
216 |
217 |
218 |
219 |
220 |
221 |
222 |
223 |
224 |
225 |
226 |
227 |
228 |
229 |
230 |
231 |
232 |
233 |
234 |
235 |
236 |
237 |
238 |
239 |
240 |
241 |
242 |
243 |
244 |
245 |
246 |
247 |
248 |
249 |
250 |
251 |
252 |
253 |
254 |
255 |
256 |
257 |
258 |
259 |
260 |
261 |
262 |
263 |
264 |
265 |
266 |
267 |
268 |
269 |
270 |
271 |
272 |
273 |
274 |
275 |
276 |
277 |
278 |
279 |
280 |
281 |
282 |
283 |
284 |
285 |
286 |
287 |
288 |
289 |
290 |
291 |
292 |
293 |
294 |
295 |
296 |
297 |
298 |
299 |
300 |
301 |
302 |
303 |
304 |
305 |
306 |
307 |
308 |
309 |
310 |
311 |
312 |
313 | true
314 |
315 |
316 |
317 |
318 |
319 |
320 |
321 |
322 |
323 |
324 |
325 |
326 |
327 |
328 |
329 |
330 |
331 |
332 |
333 |
334 |
335 |
336 |
337 |
338 |
339 |
340 |
341 |
342 |
343 |
344 |
345 |
346 |
347 |
348 |
349 |
350 |
351 |
352 |
353 |
354 |
355 |
356 |
357 |
358 |
359 |
360 |
361 |
362 |
363 |
364 |
365 |
366 |
367 |
368 |
369 |
370 |
371 |
372 |
373 |
374 |
375 |
376 |
377 |
378 |
379 |
380 |
381 |
382 |
383 |
384 |
385 |
386 |
387 |
388 |
389 |
390 |
391 |
392 |
393 |
394 |
395 |
396 |
397 |
398 |
399 |
400 |
401 |
402 |
403 |
404 |
405 |
406 |
407 |
408 |
409 |
410 |
411 |
412 |
413 |
414 |
415 |
416 |
417 |
418 |
419 |
420 |
421 |
422 |
423 |
424 |
425 |
426 |
427 |
428 |
429 |
430 |
431 |
432 |
433 |
434 |
435 |
436 |
437 |
438 |
439 |
440 |
441 |
442 |
443 |
444 |
445 |
446 |
447 |
448 |
449 |
450 |
451 |
452 |
453 |
454 |
455 |
456 |
457 |
458 | 1450654437297
459 |
460 | 1450654437297
461 |
462 |
463 |
464 |
465 |
466 |
467 |
468 |
469 |
470 |
471 |
472 |
473 |
474 |
475 |
476 |
477 |
478 |
479 |
480 |
481 |
482 |
483 |
484 |
485 |
486 |
487 |
488 |
489 |
490 |
491 |
492 |
493 |
494 |
495 |
496 |
497 |
498 |
499 |
500 |
501 |
502 |
503 |
504 |
505 |
506 |
507 |
508 |
509 |
510 |
511 |
512 |
513 |
514 |
515 |
516 |
517 |
518 |
519 |
520 |
521 |
522 |
523 |
524 |
525 |
526 |
527 |
528 |
529 |
530 |
531 |
532 |
533 |
534 |
535 |
536 |
537 |
538 |
539 |
540 |
541 |
542 |
543 |
544 |
545 |
546 |
547 |
548 |
549 |
550 |
551 |
552 |
553 |
554 |
555 |
556 |
557 |
558 |
559 |
560 |
561 |
562 |
563 |
564 |
565 |
566 |
567 |
568 |
569 |
570 |
571 |
572 |
573 |
574 |
575 |
576 |
577 |
578 |
579 |
580 |
581 |
582 |
583 |
584 |
585 |
586 |
587 |
588 |
589 |
590 |
591 |
592 |
593 |
594 |
595 |
596 |
597 |
598 |
599 |
600 |
601 |
602 |
603 |
604 |
605 |
606 |
607 |
608 |
609 |
610 |
611 |
612 |
613 |
614 |
615 |
616 |
617 |
618 |
619 |
620 |
621 |
622 |
623 |
624 |
625 |
626 |
627 |
628 |
629 |
630 |
631 |
632 |
633 |
634 |
635 |
636 |
637 |
638 |
639 |
640 |
641 |
642 |
643 |
644 |
645 |
646 |
647 |
648 |
649 |
650 |
651 |
652 |
653 |
654 |
655 |
656 |
657 |
658 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | ## Multi-Vendor Digital Marketplace Platform(Early stage development)
2 |
3 | This project is an attempt to develop a Multi-vendor digital marketplace platform on top of Django Framework and offer sellers to register, upload and sell their digital images. Development is still is early stages and not recommended for production use. Pull request with fixes and enhancements are most welcome, though.
4 |
5 | ## Features
6 | * Products
7 | * Curated Products
8 | * My product
9 | * Products rating
10 | * Image Thumbnail
11 | * Sellers
12 | * Seller dashboard
13 | * Seller side option for product management
14 | * Admin dashboard
15 | * Basic analytics
16 | * Tags
17 |
18 | ## Installation
19 |
20 | Post setting up virtualenv Run the following commands. In case you would like know how to setup virtualenv, browse through [Configure virtualenv and virtualenvwrapper](http://www.sunilsrikumar.com/2016/03/django-multi-site-setup/)
21 |
22 | ```
23 | git clone https://github.com/nescode/djangodm.git
24 | cd djangodm
25 | pip install -r requirements.txt
26 | python manage.py migrate
27 | python manage.py runserver
28 | ```
29 | The Multi-vendor Marketplace will be now accessible at http://127.0.0.1:8000/ and the admin interface
30 | at http://127.0.0.1:8000/admin/ . To login into admin panel create a superuser:
31 |
32 | ```
33 | python manage.py createsuperuser
34 | ```
35 | Give your username, email id and password to complete the user creation process.
36 |
37 | ## Paid development
38 |
39 | We love Python and Django because of its flexibility and clean code. We offer customized application development using Django. Just say hello at info@nescode.com to start a discussion.
40 |
41 | ## Django development company
42 |
43 | We are passionate technologists. We offer full stack development and consulting for organizations
44 | with Python, Django framework, Wagtail and PostgreSQL. Drop us a line at info@nescode.com to shape your idea.
45 |
--------------------------------------------------------------------------------
/analytics/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nescode/djangodm/6bc883169f34e693c948487807de2743a445978e/analytics/__init__.py
--------------------------------------------------------------------------------
/analytics/admin.py:
--------------------------------------------------------------------------------
1 | from django.contrib import admin
2 |
3 | from .models import TagView
4 |
5 | admin.site.register(TagView)
6 |
--------------------------------------------------------------------------------
/analytics/migrations/0001_initial.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | from __future__ import unicode_literals
3 |
4 | from django.db import migrations, models
5 | from django.conf import settings
6 |
7 |
8 | class Migration(migrations.Migration):
9 |
10 | dependencies = [
11 | ('tags', '0001_initial'),
12 | migrations.swappable_dependency(settings.AUTH_USER_MODEL),
13 | ]
14 |
15 | operations = [
16 | migrations.CreateModel(
17 | name='TagView',
18 | fields=[
19 | ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
20 | ('count', models.IntegerField(default=0)),
21 | ('tag', models.ForeignKey(to='tags.Tag')),
22 | ('user', models.ForeignKey(blank=True, to=settings.AUTH_USER_MODEL, null=True)),
23 | ],
24 | ),
25 | ]
26 |
--------------------------------------------------------------------------------
/analytics/migrations/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nescode/djangodm/6bc883169f34e693c948487807de2743a445978e/analytics/migrations/__init__.py
--------------------------------------------------------------------------------
/analytics/models.py:
--------------------------------------------------------------------------------
1 | from django.conf import settings
2 | from django.db import models
3 |
4 |
5 | from tags.models import Tag
6 |
7 |
8 | class TagViewManager(models.Manager):
9 | def add_count(self, user, tag):
10 | obj, created = self.model.objects.get_or_create(user=user,
11 | tag=tag)
12 | obj.count += 1
13 | obj.save()
14 | return obj
15 |
16 |
17 | class TagView(models.Model):
18 | user = models.ForeignKey(settings.AUTH_USER_MODEL, blank=True, null=True)
19 | tag = models.ForeignKey(Tag)
20 | count = models.IntegerField(default=0)
21 |
22 | objects = TagViewManager()
23 |
24 | def __unicode__(self):
25 | return str(self.tag.title)
26 |
--------------------------------------------------------------------------------
/analytics/tests.py:
--------------------------------------------------------------------------------
1 | from django.test import TestCase
2 |
3 | # Create your tests here.
4 |
--------------------------------------------------------------------------------
/analytics/views.py:
--------------------------------------------------------------------------------
1 | from django.shortcuts import render
2 |
3 | # Create your views here.
4 |
--------------------------------------------------------------------------------
/billing/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nescode/djangodm/6bc883169f34e693c948487807de2743a445978e/billing/__init__.py
--------------------------------------------------------------------------------
/billing/admin.py:
--------------------------------------------------------------------------------
1 | from django.contrib import admin
2 |
3 |
4 | from .models import Transaction
5 |
6 | admin.site.register(Transaction)
7 |
--------------------------------------------------------------------------------
/billing/migrations/0001_initial.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | from __future__ import unicode_literals
3 |
4 | from django.db import migrations, models
5 | from django.conf import settings
6 |
7 |
8 | class Migration(migrations.Migration):
9 |
10 | dependencies = [
11 | migrations.swappable_dependency(settings.AUTH_USER_MODEL),
12 | ('products', '0014_remove_thumbnail_user'),
13 | ]
14 |
15 | operations = [
16 | migrations.CreateModel(
17 | name='Transaction',
18 | fields=[
19 | ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
20 | ('price', models.DecimalField(default=9.99, null=True, max_digits=100, decimal_places=2)),
21 | ('timestamp', models.DateTimeField(auto_now_add=True)),
22 | ('success', models.BooleanField(default=True)),
23 | ('product', models.ForeignKey(to='products.Product')),
24 | ('user', models.ForeignKey(to=settings.AUTH_USER_MODEL)),
25 | ],
26 | ),
27 | ]
28 |
--------------------------------------------------------------------------------
/billing/migrations/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nescode/djangodm/6bc883169f34e693c948487807de2743a445978e/billing/migrations/__init__.py
--------------------------------------------------------------------------------
/billing/models.py:
--------------------------------------------------------------------------------
1 | from django.conf import settings
2 | from django.db import models
3 |
4 | from products.models import Product
5 |
6 | class Transaction(models.Model):
7 | user = models.ForeignKey(settings.AUTH_USER_MODEL)
8 | product = models.ForeignKey(Product)
9 | price = models.DecimalField(max_digits=100, decimal_places=2, default=9.99, null=True,)
10 | timestamp = models.DateTimeField(auto_now_add=True, auto_now=False)
11 | success = models.BooleanField(default=True)
12 |
13 | def __unicode__(self):
14 | return "%s" %(self.id)
15 |
--------------------------------------------------------------------------------
/billing/tests.py:
--------------------------------------------------------------------------------
1 | from django.test import TestCase
2 |
3 | # Create your tests here.
4 |
--------------------------------------------------------------------------------
/billing/views.py:
--------------------------------------------------------------------------------
1 | from django.shortcuts import render
2 |
3 | # Create your views here.
4 |
--------------------------------------------------------------------------------
/checkout/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nescode/djangodm/6bc883169f34e693c948487807de2743a445978e/checkout/__init__.py
--------------------------------------------------------------------------------
/checkout/admin.py:
--------------------------------------------------------------------------------
1 | from django.contrib import admin
2 |
3 | # Register your models here.
4 |
--------------------------------------------------------------------------------
/checkout/migrations/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nescode/djangodm/6bc883169f34e693c948487807de2743a445978e/checkout/migrations/__init__.py
--------------------------------------------------------------------------------
/checkout/models.py:
--------------------------------------------------------------------------------
1 | from django.db import models
2 |
--------------------------------------------------------------------------------
/checkout/tests.py:
--------------------------------------------------------------------------------
1 | from django.test import TestCase
2 |
3 | # Create your tests here.
4 |
--------------------------------------------------------------------------------
/checkout/views.py:
--------------------------------------------------------------------------------
1 | import datetime
2 |
3 | from django.http import HttpResponse, JsonResponse, Http404
4 | from django.views.generic import View
5 | from django.shortcuts import render
6 |
7 | from products.models import Product, MyProducts
8 | # Create your views here.
9 | from djangodm.mixins import AjaxRequiredMixin
10 |
11 | from billing.models import Transaction
12 |
13 | class CheckoutAjaxView(AjaxRequiredMixin, View):
14 | def post(self, request, *args, **kwargs):
15 | # if request.is_ajax():
16 | # raise Http404
17 | if not request.user.is_authenticated():
18 | return JsonResponse({}, status=401)
19 | # credit card required **
20 |
21 | user = request.user
22 | product_id = request.POST.get("product_id")
23 | exists = Product.objects.filter(id=product_id).exists()
24 | if not exists:
25 | return JsonResponse({}, status=404)
26 |
27 | try:
28 | product_obj = Product.object.get(id=product_id)
29 | except:
30 | product_obj = Product.objects.filter(id=product_id).first()
31 |
32 | # run Transaction
33 | trans_obj = Transaction.objects.create(
34 | user = request.user,
35 | product = product_obj,
36 | price = product_obj.get_price,
37 | )
38 |
39 | my_products = MyProducts.objects.get_or_create(user=request.user)[0]
40 | my_products.products.add(product_obj)
41 |
42 | download_link = product_obj.get_download()
43 | preview_link = download_link + "?preview=True"
44 | data = {
45 | "download": download_link,
46 | "preview": preview_link,
47 | }
48 | return JsonResponse(data)
49 | # raise Http404
50 |
51 |
52 |
53 | class CheckoutTestView(View):
54 | def post(self, request, *args, **kwargs):
55 | print request.POST.get("testData")
56 | if request.is_ajax():
57 | # raise Http404
58 | if not request.user.is_authenticated():
59 | data = {
60 | "works": False,
61 | }
62 | return JsonResponse(data, status=401)
63 | data = {
64 | "works": True,
65 | "time": datetime.datetime.now(),
66 | }
67 | return JsonResponse(data)
68 | return HttpResponse("hello there")
69 |
70 | def get(self, request, *args, **kwargs):
71 | template = "checkout/test.html"
72 | context = {}
73 | return render(request, template, context)
74 |
--------------------------------------------------------------------------------
/dashboard/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nescode/djangodm/6bc883169f34e693c948487807de2743a445978e/dashboard/__init__.py
--------------------------------------------------------------------------------
/dashboard/admin.py:
--------------------------------------------------------------------------------
1 | from django.contrib import admin
2 |
--------------------------------------------------------------------------------
/dashboard/migrations/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nescode/djangodm/6bc883169f34e693c948487807de2743a445978e/dashboard/migrations/__init__.py
--------------------------------------------------------------------------------
/dashboard/models.py:
--------------------------------------------------------------------------------
1 | from django.db import models
2 |
--------------------------------------------------------------------------------
/dashboard/tests.py:
--------------------------------------------------------------------------------
1 | from django.test import TestCase
2 |
3 | # Create your tests here.
4 |
--------------------------------------------------------------------------------
/dashboard/views.py:
--------------------------------------------------------------------------------
1 | import random
2 |
3 | from django.views.generic import View
4 | from django.shortcuts import render
5 |
6 |
7 | from products.models import Product, CuratedProducts
8 |
9 | class DashboardView(View):
10 | def get(self, request, *args, **kwargs):
11 | tag_views = None
12 | products = None
13 | top_tags = None
14 | curated = CuratedProducts.objects.filter(active=True).order_by("?")
15 | try:
16 | tag_views = request.user.tagview_set.all().order_by("-count")[:5]
17 | except:
18 | pass
19 |
20 | owned = None
21 |
22 | try:
23 | owned = request.user.myproducts.products.all()
24 | except:
25 | pass
26 |
27 | if tag_views:
28 | top_tags = [x.tag for x in tag_views]
29 | products = Product.objects.filter(tag__in=top_tags)
30 | if owned:
31 | products = products.exclude(pk__in=owned)
32 |
33 | if products.count() < 10:
34 | products = Product.objects.all().order_by("?")
35 | if owned:
36 | products = products.exclude(pk__in=owned)
37 | products = products[:10]
38 | else:
39 | products = products.distinct()
40 | products = sorted(products, key= lambda x: random.random())
41 |
42 | context = {
43 | "products": products,
44 | "top_tags": top_tags,
45 | "curated": curated,
46 | }
47 | return render(request, "dashboard/view.html", context)
48 |
--------------------------------------------------------------------------------
/djangodm/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nescode/djangodm/6bc883169f34e693c948487807de2743a445978e/djangodm/__init__.py
--------------------------------------------------------------------------------
/djangodm/decorators.py:
--------------------------------------------------------------------------------
1 | from django.http import Http404
2 |
3 |
4 | def ajax_required(function):
5 | """
6 | This is a doc string
7 | """
8 | def wrap(request, *args, **kwargs):
9 | if not request.is_ajax():
10 | raise Http404
11 | return function(request, *args, **kwargs)
12 | wrap.__doc__ = function.__doc__
13 | wrap.__name__ = function.__name__
14 | return wrap
15 |
--------------------------------------------------------------------------------
/djangodm/mixins.py:
--------------------------------------------------------------------------------
1 | from django.contrib.admin.views.decorators import staff_member_required
2 | from django.contrib.auth.decorators import login_required
3 | from django.utils.decorators import method_decorator
4 | from django.shortcuts import get_object_or_404
5 |
6 | from .decorators import ajax_required
7 |
8 | class AjaxRequiredMixin(object):
9 | @method_decorator(ajax_required)
10 | def dispatch(self, request, *args, **kwargs):
11 | return super(AjaxRequiredMixin, self).dispatch(request, *args, **kwargs)
12 |
13 |
14 | class LoginRequiredMixin(object):
15 | @method_decorator(login_required)
16 | def dispatch(self, request, *args, **kwargs):
17 | return super(LoginRequiredMixin, self).dispatch(request, *args, **kwargs)
18 |
19 | class StaffRequiredMixin(object):
20 | @method_decorator(staff_member_required)
21 | def dispatch(self, request, *args, **kwargs):
22 | return super(StaffRequiredMixin, self).dispatch(request, *args, **kwargs)
23 |
24 | class MultiSlugMixin(object):
25 | model = None
26 |
27 | def get_object(self, *args, **kwargs):
28 | slug = self.kwargs.get("slug")
29 | ModelClass = self.model
30 | if slug is not None:
31 | try:
32 | obj = get_object_or_404(ModelClass, slug=slug)
33 | except ModelClass.MultipleObjectsReturned:
34 | obj = ModelClass.objects.filter(slug=slug).order_by("-title").first()
35 | else:
36 | obj = super(MultiSlugMixin, self).get_object(*args, **kwargs)
37 | return obj
38 |
39 |
40 | class SubmitBtnMixin(object):
41 | submit_btn = None
42 |
43 | def get_context_data(self, *args, **kwargs):
44 | context = super(SubmitBtnMixin, self).get_context_data(*args, **kwargs)
45 | context["submit_btn"] = self.submit_btn
46 | return context
47 |
--------------------------------------------------------------------------------
/djangodm/settings/__init__.py:
--------------------------------------------------------------------------------
1 | from .dev import *
2 |
--------------------------------------------------------------------------------
/djangodm/settings/base.py:
--------------------------------------------------------------------------------
1 | """
2 | Django settings for djangodm project.
3 |
4 | Generated by 'django-admin startproject' using Django 1.8.7.
5 |
6 | For more information on this file, see
7 | https://docs.djangoproject.com/en/1.8/topics/settings/
8 |
9 | For the full list of settings and their values, see
10 | https://docs.djangoproject.com/en/1.8/ref/settings/
11 | """
12 |
13 | # Build paths inside the project like this: os.path.join(BASE_DIR, ...)
14 | import os
15 |
16 | PROJECT_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
17 | BASE_DIR = os.path.dirname(PROJECT_DIR)
18 |
19 | # Quick-start development settings - unsuitable for production
20 | # See https://docs.djangoproject.com/en/1.8/howto/deployment/checklist/
21 |
22 | # SECURITY WARNING: don't run with debug turned on in production!
23 |
24 | ALLOWED_HOSTS = []
25 |
26 |
27 | # Application definition
28 |
29 | INSTALLED_APPS = (
30 | 'django.contrib.admin',
31 | 'django.contrib.auth',
32 | 'django.contrib.contenttypes',
33 | 'django.contrib.sessions',
34 | 'django.contrib.messages',
35 | 'django.contrib.staticfiles',
36 |
37 | # Custom Apps
38 | 'analytics',
39 | 'billing',
40 | 'products',
41 | 'sellers',
42 | 'tags',
43 | 'dashboard',
44 | )
45 |
46 | MIDDLEWARE_CLASSES = (
47 | 'django.contrib.sessions.middleware.SessionMiddleware',
48 | 'django.middleware.common.CommonMiddleware',
49 | 'django.middleware.csrf.CsrfViewMiddleware',
50 | 'django.contrib.auth.middleware.AuthenticationMiddleware',
51 | 'django.contrib.auth.middleware.SessionAuthenticationMiddleware',
52 | 'django.contrib.messages.middleware.MessageMiddleware',
53 | 'django.middleware.clickjacking.XFrameOptionsMiddleware',
54 | 'django.middleware.security.SecurityMiddleware',
55 | )
56 |
57 | ROOT_URLCONF = 'djangodm.urls'
58 |
59 | TEMPLATES = [
60 | {
61 | 'BACKEND': 'django.template.backends.django.DjangoTemplates',
62 | 'DIRS': [os.path.join(BASE_DIR, 'templates')],
63 | 'APP_DIRS': True,
64 | 'OPTIONS': {
65 | 'context_processors': [
66 | 'django.template.context_processors.debug',
67 | 'django.template.context_processors.request',
68 | 'django.contrib.auth.context_processors.auth',
69 | 'django.contrib.messages.context_processors.messages',
70 | ],
71 | },
72 | },
73 | ]
74 |
75 | WSGI_APPLICATION = 'djangodm.wsgi.application'
76 |
77 |
78 | # Database
79 | # https://docs.djangoproject.com/en/1.8/ref/settings/#databases
80 |
81 | DATABASES = {
82 | 'default': {
83 | 'ENGINE': 'django.db.backends.sqlite3',
84 | 'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
85 | }
86 | }
87 |
88 | # DATABASES = {
89 | # 'default': {
90 | # 'ENGINE': 'django.db.backends.postgresql_psycopg2',
91 | # 'NAME': 'djangodm',
92 | # 'USER': 'djangodmadmin',
93 | # 'PASSWORD': 'Indian@1979',
94 | # 'HOST': 'localhost',
95 | # 'PORT': '',
96 | # }
97 | # }
98 |
99 | # Internationalization
100 | # https://docs.djangoproject.com/en/1.8/topics/i18n/
101 |
102 | LANGUAGE_CODE = 'en-us'
103 |
104 | TIME_ZONE = 'UTC'
105 |
106 | USE_I18N = True
107 |
108 | USE_L10N = True
109 |
110 | USE_TZ = True
111 |
112 |
113 | # Static files (CSS, JavaScript, Images)
114 | # https://docs.djangoproject.com/en/1.8/howto/static-files/
115 |
116 | STATIC_URL = '/static/'
117 | MEDIA_URL = '/media/'
118 |
119 | STATICFILES_DIRS = (
120 | os.path.join(BASE_DIR, "static"),
121 | )
122 |
123 | STATIC_ROOT = os.path.join(BASE_DIR, "static_cdn", "staticfiles")
124 | MEDIA_ROOT = os.path.join(BASE_DIR, "static_cdn", "media")
125 |
126 | PROTECTED_ROOT = os.path.join(BASE_DIR, "static_cdn", "protected")
127 |
--------------------------------------------------------------------------------
/djangodm/settings/dev.py:
--------------------------------------------------------------------------------
1 | from .base import *
2 |
3 |
4 | # SECURITY WARNING: don't run with debug turned on in production!
5 | DEBUG = True
6 |
7 | for template_engine in TEMPLATES:
8 | template_engine['OPTIONS']['debug'] = True
9 |
10 | # SECURITY WARNING: keep the secret key used in production secret!
11 | SECRET_KEY = 'bklsqv2q*250(ak=n$(era!h7ndoe&))rpxjt1==t6p@e=2^*!'
12 |
13 |
14 | EMAIL_BACKEND = 'django.core.mail.backends.console.EmailBackend'
15 |
16 |
17 | try:
18 | from .local import *
19 | except ImportError:
20 | pass
21 |
--------------------------------------------------------------------------------
/djangodm/settings/production.py:
--------------------------------------------------------------------------------
1 | from .base import *
2 |
3 |
4 | DEBUG = False
5 |
6 | try:
7 | from .local import *
8 | except ImportError:
9 | pass
10 |
--------------------------------------------------------------------------------
/djangodm/urls.py:
--------------------------------------------------------------------------------
1 | """djangodm URL Configuration
2 |
3 | The `urlpatterns` list routes URLs to views. For more information please see:
4 | https://docs.djangoproject.com/en/1.8/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: url(r'^$', 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: url(r'^$', Home.as_view(), name='home')
12 | Including another URLconf
13 | 1. Add an import: from blog import urls as blog_urls
14 | 2. Add a URL to urlpatterns: url(r'^blog/', include(blog_urls))
15 | """
16 | from django.conf import settings
17 | from django.conf.urls import include, url
18 | from django.conf.urls.static import static
19 | from django.contrib import admin
20 |
21 | from checkout.views import CheckoutTestView, CheckoutAjaxView
22 | from dashboard.views import DashboardView
23 | from products.views import UserLibraryListView
24 |
25 | urlpatterns = [
26 | url(r'^$', DashboardView.as_view(), name='dashboard'),
27 | url(r'^test/$', CheckoutTestView.as_view(), name='test'),
28 | url(r'^checkout/$', CheckoutAjaxView.as_view(), name='checkout'),
29 | url(r'^admin/', include(admin.site.urls)),
30 | url(r'^products/', include("products.urls", namespace='products')),
31 | url(r'^seller/', include("sellers.urls", namespace='sellers')),
32 | url(r'^tags/',include("tags.urls", namespace='tags')),
33 | url(r'^library/', UserLibraryListView.as_view(), name='library'),
34 | ]
35 |
36 | if settings.DEBUG:
37 | urlpatterns += static(settings.STATIC_URL, document_root=settings.STATIC_ROOT)
38 | urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
39 |
--------------------------------------------------------------------------------
/djangodm/wsgi.py:
--------------------------------------------------------------------------------
1 | """
2 | WSGI config for djangodm 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/1.8/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", "djangodm.settings")
15 |
16 | application = get_wsgi_application()
17 |
--------------------------------------------------------------------------------
/manage.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | import os
3 | import sys
4 |
5 | if __name__ == "__main__":
6 | os.environ.setdefault("DJANGO_SETTINGS_MODULE", "djangodm.settings")
7 |
8 | from django.core.management import execute_from_command_line
9 |
10 | execute_from_command_line(sys.argv)
11 |
--------------------------------------------------------------------------------
/products/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nescode/djangodm/6bc883169f34e693c948487807de2743a445978e/products/__init__.py
--------------------------------------------------------------------------------
/products/admin.py:
--------------------------------------------------------------------------------
1 | from django.contrib import admin
2 |
3 | from .models import (
4 | Product,
5 | MyProducts,
6 | Thumbnail,
7 | ProductRating,
8 | CuratedProducts,
9 | )
10 |
11 | class ThumbnailInline(admin.TabularInline):
12 | extra = 1
13 | model = Thumbnail
14 |
15 | class ProductAdmin(admin.ModelAdmin):
16 | inlines = [ThumbnailInline]
17 | list_display = ["__unicode__", "description", "price", "sale_price"]
18 | search_fields = ["title", "description"]
19 | list_filter = ["price", "sale_price"]
20 | list_editable = ["sale_price"]
21 | class Meta:
22 | model = Product
23 |
24 |
25 | admin.site.register(Product, ProductAdmin)
26 |
27 | admin.site.register(MyProducts)
28 |
29 | admin.site.register(Thumbnail)
30 |
31 | admin.site.register(ProductRating)
32 |
33 | admin.site.register(CuratedProducts)
34 |
--------------------------------------------------------------------------------
/products/forms.py:
--------------------------------------------------------------------------------
1 | from django import forms
2 | from django.utils.text import slugify
3 | from .models import Product
4 |
5 | PUBLISH_CHOICES = (
6 | ('publish', "Publish"),
7 | ('draft', "Draft")
8 | )
9 | class ProductAddForm(forms.Form):
10 | title = forms.CharField(label='Your Title', widget=forms.TextInput(
11 | attrs={
12 | "class": "custom-class",
13 | "placeholder": "Title",
14 | }
15 | ))
16 | description = forms.CharField(widget=forms.Textarea(
17 | attrs={
18 | "class": "my-custom-class",
19 | "placeholder": "Description",
20 | "some-attr": "this",
21 | }
22 | )) # This might create issue.
23 | price = forms.DecimalField()
24 | publish = forms.ChoiceField(widget=forms.RadioSelect, choices=PUBLISH_CHOICES, required=False)
25 |
26 | def clean_price(self):
27 | price = self.cleaned_data.get("price")
28 | if price <= 1.00:
29 | raise forms.ValidationError("Price must be greater than $1.")
30 | elif price >= 100:
31 | raise forms.ValidationError("Price must be less than $100.")
32 | else:
33 | return price
34 |
35 | def clean_title(self):
36 | title = self.cleaned_data.get("title")
37 | if len(title) > 3:
38 | return title
39 | else:
40 | raise forms.ValidationError("Title must be greater than 3 characters long.")
41 |
42 | class ProductModelForm(forms.ModelForm):
43 | tags = forms.CharField(label='Related Tags', required=False)
44 | publish = forms.ChoiceField(widget=forms.RadioSelect, choices=PUBLISH_CHOICES, required=False)
45 |
46 | class Meta:
47 | model = Product
48 | fields = [
49 | "title",
50 | "description",
51 | "price",
52 | "media",
53 | ]
54 | widgets = {
55 | "description": forms.Textarea(
56 | attrs={
57 | "placeholder": "New Description"
58 | }
59 | ),
60 | "title": forms.TextInput(
61 | attrs={
62 | "placeholder": "Title"
63 | }
64 | )
65 | }
66 |
67 | def clean(self, *args, **kwargs):
68 | cleaned_data = super(ProductModelForm, self).clean(*args, **kwargs)
69 | return cleaned_data
70 |
71 |
72 | def clean_price(self):
73 | price = self.cleaned_data.get("price")
74 | if price <= 1.00:
75 | raise forms.ValidationError("Price must be greater than $1.")
76 | elif price >= 99.99:
77 | raise forms.ValidationError("Price must be less than $100.")
78 | else:
79 | return price
80 |
81 | def clean_title(self):
82 | title = self.cleaned_data.get("title")
83 | if len(title) > 3:
84 | return title
85 | else:
86 | raise forms.ValidationError("Title must be greater than 3 characters long.")
87 |
--------------------------------------------------------------------------------
/products/migrations/0001_initial.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | from __future__ import unicode_literals
3 |
4 | from django.db import migrations, models
5 |
6 |
7 | class Migration(migrations.Migration):
8 |
9 | dependencies = [
10 | ]
11 |
12 | operations = [
13 | migrations.CreateModel(
14 | name='Product',
15 | fields=[
16 | ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
17 | ('title', models.CharField(max_length=30)),
18 | ('slug', models.SlugField(blank=True)),
19 | ('description', models.TextField()),
20 | ('price', models.DecimalField(default=9.99, null=True, max_digits=100, decimal_places=2)),
21 | ('sale_price', models.DecimalField(default=6.99, null=True, max_digits=100, decimal_places=2, blank=True)),
22 | ],
23 | ),
24 | ]
25 |
--------------------------------------------------------------------------------
/products/migrations/0002_auto_20151225_1715.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | from __future__ import unicode_literals
3 |
4 | from django.db import migrations, models
5 |
6 |
7 | class Migration(migrations.Migration):
8 |
9 | dependencies = [
10 | ('products', '0001_initial'),
11 | ]
12 |
13 | operations = [
14 | migrations.AlterField(
15 | model_name='product',
16 | name='slug',
17 | field=models.SlugField(unique=True, blank=True),
18 | ),
19 | ]
20 |
--------------------------------------------------------------------------------
/products/migrations/0003_auto_20151225_1725.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | from __future__ import unicode_literals
3 |
4 | from django.db import migrations, models
5 |
6 |
7 | class Migration(migrations.Migration):
8 |
9 | dependencies = [
10 | ('products', '0002_auto_20151225_1715'),
11 | ]
12 |
13 | operations = [
14 | migrations.AlterField(
15 | model_name='product',
16 | name='slug',
17 | field=models.SlugField(unique=True, null=True, blank=True),
18 | ),
19 | ]
20 |
--------------------------------------------------------------------------------
/products/migrations/0004_auto_20151226_0250.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | from __future__ import unicode_literals
3 |
4 | from django.db import migrations, models
5 |
6 |
7 | class Migration(migrations.Migration):
8 |
9 | dependencies = [
10 | ('products', '0003_auto_20151225_1725'),
11 | ]
12 |
13 | operations = [
14 | migrations.AlterField(
15 | model_name='product',
16 | name='slug',
17 | field=models.SlugField(unique=True, blank=True),
18 | ),
19 | ]
20 |
--------------------------------------------------------------------------------
/products/migrations/0005_product_user.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | from __future__ import unicode_literals
3 |
4 | from django.db import migrations, models
5 | from django.conf import settings
6 |
7 |
8 | class Migration(migrations.Migration):
9 |
10 | dependencies = [
11 | migrations.swappable_dependency(settings.AUTH_USER_MODEL),
12 | ('products', '0004_auto_20151226_0250'),
13 | ]
14 |
15 | operations = [
16 | migrations.AddField(
17 | model_name='product',
18 | name='user',
19 | field=models.ForeignKey(default=1, to=settings.AUTH_USER_MODEL),
20 | preserve_default=False,
21 | ),
22 | ]
23 |
--------------------------------------------------------------------------------
/products/migrations/0006_product_manager.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | from __future__ import unicode_literals
3 |
4 | from django.db import migrations, models
5 | from django.conf import settings
6 |
7 |
8 | class Migration(migrations.Migration):
9 |
10 | dependencies = [
11 | migrations.swappable_dependency(settings.AUTH_USER_MODEL),
12 | ('products', '0005_product_user'),
13 | ]
14 |
15 | operations = [
16 | migrations.AddField(
17 | model_name='product',
18 | name='manager',
19 | field=models.ManyToManyField(related_name='managers_products', to=settings.AUTH_USER_MODEL),
20 | ),
21 | ]
22 |
--------------------------------------------------------------------------------
/products/migrations/0007_auto_20151226_0646.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | from __future__ import unicode_literals
3 |
4 | from django.db import migrations, models
5 | from django.conf import settings
6 |
7 |
8 | class Migration(migrations.Migration):
9 |
10 | dependencies = [
11 | migrations.swappable_dependency(settings.AUTH_USER_MODEL),
12 | ('products', '0006_product_manager'),
13 | ]
14 |
15 | operations = [
16 | migrations.RemoveField(
17 | model_name='product',
18 | name='manager',
19 | ),
20 | migrations.AddField(
21 | model_name='product',
22 | name='managers',
23 | field=models.ManyToManyField(related_name='managers_products', to=settings.AUTH_USER_MODEL, blank=True),
24 | ),
25 | ]
26 |
--------------------------------------------------------------------------------
/products/migrations/0008_product_media.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | from __future__ import unicode_literals
3 |
4 | from django.db import migrations, models
5 | import products.models
6 |
7 |
8 | class Migration(migrations.Migration):
9 |
10 | dependencies = [
11 | ('products', '0007_auto_20151226_0646'),
12 | ]
13 |
14 | operations = [
15 | migrations.AddField(
16 | model_name='product',
17 | name='media',
18 | field=models.FileField(null=True, upload_to=products.models.download_media_location, blank=True),
19 | ),
20 | ]
21 |
--------------------------------------------------------------------------------
/products/migrations/0009_auto_20151227_0602.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | from __future__ import unicode_literals
3 |
4 | from django.db import migrations, models
5 | import django.core.files.storage
6 | import products.models
7 |
8 |
9 | class Migration(migrations.Migration):
10 |
11 | dependencies = [
12 | ('products', '0008_product_media'),
13 | ]
14 |
15 | operations = [
16 | migrations.AlterField(
17 | model_name='product',
18 | name='media',
19 | field=models.FileField(storage=django.core.files.storage.FileSystemStorage(location=b'/Users/NESCODE/project/djangodm/static_cdn/protected'), null=True, upload_to=products.models.download_media_location, blank=True),
20 | ),
21 | ]
22 |
--------------------------------------------------------------------------------
/products/migrations/0010_myproducts.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | from __future__ import unicode_literals
3 |
4 | from django.db import migrations, models
5 | from django.conf import settings
6 |
7 |
8 | class Migration(migrations.Migration):
9 |
10 | dependencies = [
11 | migrations.swappable_dependency(settings.AUTH_USER_MODEL),
12 | ('products', '0009_auto_20151227_0602'),
13 | ]
14 |
15 | operations = [
16 | migrations.CreateModel(
17 | name='MyProducts',
18 | fields=[
19 | ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
20 | ('products', models.ManyToManyField(to='products.Product', blank=True)),
21 | ('user', models.OneToOneField(to=settings.AUTH_USER_MODEL)),
22 | ],
23 | ),
24 | ]
25 |
--------------------------------------------------------------------------------
/products/migrations/0011_auto_20151231_0536.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | from __future__ import unicode_literals
3 |
4 | from django.db import migrations, models
5 | from django.conf import settings
6 | import products.models
7 |
8 |
9 | class Migration(migrations.Migration):
10 |
11 | dependencies = [
12 | migrations.swappable_dependency(settings.AUTH_USER_MODEL),
13 | ('products', '0010_myproducts'),
14 | ]
15 |
16 | operations = [
17 | migrations.CreateModel(
18 | name='Thumbnail',
19 | fields=[
20 | ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
21 | ('height', models.CharField(max_length=20, null=True, blank=True)),
22 | ('width', models.CharField(max_length=20, null=True, blank=True)),
23 | ('media', models.ImageField(height_field=b'height', width_field=b'width', null=True, upload_to=products.models.download_media_location, blank=True)),
24 | ('product', models.ForeignKey(to='products.Product')),
25 | ('user', models.ForeignKey(to=settings.AUTH_USER_MODEL)),
26 | ],
27 | ),
28 | migrations.AlterModelOptions(
29 | name='myproducts',
30 | options={'verbose_name': 'My products', 'verbose_name_plural': 'My Products'},
31 | ),
32 | ]
33 |
--------------------------------------------------------------------------------
/products/migrations/0012_auto_20151231_0600.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | from __future__ import unicode_literals
3 |
4 | from django.db import migrations, models
5 | import products.models
6 |
7 |
8 | class Migration(migrations.Migration):
9 |
10 | dependencies = [
11 | ('products', '0011_auto_20151231_0536'),
12 | ]
13 |
14 | operations = [
15 | migrations.AddField(
16 | model_name='thumbnail',
17 | name='type',
18 | field=models.CharField(default=b'hd', max_length=20, choices=[(b'hd', b'HD'), (b'sd', b'SD'), (b'micro', b'Micro')]),
19 | ),
20 | migrations.AlterField(
21 | model_name='thumbnail',
22 | name='media',
23 | field=models.ImageField(height_field=b'height', width_field=b'width', null=True, upload_to=products.models.thumbnail_location, blank=True),
24 | ),
25 | ]
26 |
--------------------------------------------------------------------------------
/products/migrations/0013_auto_20151231_0634.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | from __future__ import unicode_literals
3 |
4 | from django.db import migrations, models
5 | import django.core.files.storage
6 | import products.models
7 |
8 |
9 | class Migration(migrations.Migration):
10 |
11 | dependencies = [
12 | ('products', '0012_auto_20151231_0600'),
13 | ]
14 |
15 | operations = [
16 | migrations.AlterField(
17 | model_name='product',
18 | name='media',
19 | field=models.ImageField(storage=django.core.files.storage.FileSystemStorage(location=b'/Users/NESCODE/project/djangodm/static_cdn/protected'), null=True, upload_to=products.models.download_media_location, blank=True),
20 | ),
21 | ]
22 |
--------------------------------------------------------------------------------
/products/migrations/0014_remove_thumbnail_user.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | from __future__ import unicode_literals
3 |
4 | from django.db import migrations, models
5 |
6 |
7 | class Migration(migrations.Migration):
8 |
9 | dependencies = [
10 | ('products', '0013_auto_20151231_0634'),
11 | ]
12 |
13 | operations = [
14 | migrations.RemoveField(
15 | model_name='thumbnail',
16 | name='user',
17 | ),
18 | ]
19 |
--------------------------------------------------------------------------------
/products/migrations/0015_product_sale_active.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | from __future__ import unicode_literals
3 |
4 | from django.db import migrations, models
5 |
6 |
7 | class Migration(migrations.Migration):
8 |
9 | dependencies = [
10 | ('products', '0014_remove_thumbnail_user'),
11 | ]
12 |
13 | operations = [
14 | migrations.AddField(
15 | model_name='product',
16 | name='sale_active',
17 | field=models.BooleanField(default=False),
18 | ),
19 | ]
20 |
--------------------------------------------------------------------------------
/products/migrations/0016_auto_20160224_1315.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | from __future__ import unicode_literals
3 |
4 | from django.db import migrations, models
5 |
6 |
7 | class Migration(migrations.Migration):
8 |
9 | dependencies = [
10 | ('sellers', '0001_initial'),
11 | ('products', '0015_product_sale_active'),
12 | ]
13 |
14 | operations = [
15 | migrations.RemoveField(
16 | model_name='product',
17 | name='managers',
18 | ),
19 | migrations.RemoveField(
20 | model_name='product',
21 | name='user',
22 | ),
23 | migrations.AddField(
24 | model_name='product',
25 | name='seller',
26 | field=models.ForeignKey(default=2, to='sellers.SellerAccount'),
27 | preserve_default=False,
28 | ),
29 | ]
30 |
--------------------------------------------------------------------------------
/products/migrations/0017_auto_20160318_2334.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | from __future__ import unicode_literals
3 |
4 | from django.db import migrations, models
5 | import django.core.files.storage
6 | import products.models
7 | from django.conf import settings
8 |
9 |
10 | class Migration(migrations.Migration):
11 |
12 | dependencies = [
13 | migrations.swappable_dependency(settings.AUTH_USER_MODEL),
14 | ('products', '0016_auto_20160224_1315'),
15 | ]
16 |
17 | operations = [
18 | migrations.CreateModel(
19 | name='ProductRating',
20 | fields=[
21 | ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
22 | ('rating', models.IntegerField(null=True, blank=True)),
23 | ('verified', models.BooleanField(default=False)),
24 | ],
25 | ),
26 | migrations.AlterField(
27 | model_name='product',
28 | name='media',
29 | field=models.ImageField(storage=django.core.files.storage.FileSystemStorage(location=b'/Users/NESCODE/project/githubsunil/djangodm/static_cdn/protected'), null=True, upload_to=products.models.download_media_location, blank=True),
30 | ),
31 | migrations.AddField(
32 | model_name='productrating',
33 | name='product',
34 | field=models.ForeignKey(to='products.Product'),
35 | ),
36 | migrations.AddField(
37 | model_name='productrating',
38 | name='user',
39 | field=models.ForeignKey(to=settings.AUTH_USER_MODEL),
40 | ),
41 | ]
42 |
--------------------------------------------------------------------------------
/products/migrations/0018_curatedproducts.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | from __future__ import unicode_literals
3 |
4 | from django.db import migrations, models
5 | from django.conf import settings
6 |
7 |
8 | class Migration(migrations.Migration):
9 |
10 | dependencies = [
11 | migrations.swappable_dependency(settings.AUTH_USER_MODEL),
12 | ('products', '0017_auto_20160318_2334'),
13 | ]
14 |
15 | operations = [
16 | migrations.CreateModel(
17 | name='CuratedProducts',
18 | fields=[
19 | ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
20 | ('section_name', models.CharField(max_length=120)),
21 | ('active', models.BooleanField(default=True)),
22 | ('products', models.ManyToManyField(to='products.Product', blank=True)),
23 | ('user', models.ForeignKey(to=settings.AUTH_USER_MODEL)),
24 | ],
25 | ),
26 | ]
27 |
--------------------------------------------------------------------------------
/products/migrations/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nescode/djangodm/6bc883169f34e693c948487807de2743a445978e/products/migrations/__init__.py
--------------------------------------------------------------------------------
/products/mixins.py:
--------------------------------------------------------------------------------
1 | from django.http import Http404
2 |
3 | from djangodm.mixins import LoginRequiredMixin
4 | from sellers.mixins import SellerAccountMixin
5 |
6 | class ProductManagerMixin(SellerAccountMixin, object):
7 | def get_object(self, *args, **kwargs):
8 | seller = self.get_account()
9 | obj = super(ProductManagerMixin, self).get_object(*args, **kwargs)
10 | try:
11 | obj.seller == seller
12 | except:
13 | raise Http404
14 | if obj.seller == seller:
15 | return obj
16 | else:
17 | raise Http404
18 |
--------------------------------------------------------------------------------
/products/models.py:
--------------------------------------------------------------------------------
1 | from django.conf import settings
2 | from django.core.files.storage import FileSystemStorage
3 | from django.db import models
4 | from django.db.models.signals import pre_save, post_save
5 | from django.core.urlresolvers import reverse
6 | from django.utils.text import slugify
7 |
8 | from sellers.models import SellerAccount
9 |
10 | def download_media_location(instance, filename):
11 | return "%s/%s" %(instance.slug, filename)
12 |
13 | class Product(models.Model):
14 | seller = models.ForeignKey(SellerAccount)
15 | media = models.ImageField(blank=True,
16 | null=True,
17 | upload_to=download_media_location,
18 | storage=FileSystemStorage(location=settings.PROTECTED_ROOT))
19 | title = models.CharField(max_length=30)
20 | slug = models.SlugField(blank=True, unique=True)
21 | description = models.TextField()
22 | price = models.DecimalField(max_digits=100, decimal_places=2, default=9.99, null=True) # 100.00
23 | sale_active = models.BooleanField(default=False)
24 | sale_price = models.DecimalField(max_digits=100, decimal_places=2, default=6.99, null=True, blank=True) # 100.00
25 |
26 | def __unicode__(self):
27 | return self.title
28 |
29 | def get_absolute_url(self):
30 | view_name = "products:detail_slug"
31 | return reverse(view_name, kwargs={"slug": self.slug})
32 |
33 | def get_edit_url(self):
34 | view_name = "sellers:product_edit"
35 | return reverse(view_name, kwargs={"pk": self.id})
36 |
37 | def get_download(self):
38 | view_name = "products:download_slug"
39 | url = reverse(view_name, kwargs={"slug": self.slug})
40 | return url
41 |
42 | @property
43 | def get_price(self):
44 | if self.sale_price and self.sale_active:
45 | return self.sale_price
46 | return self.price
47 |
48 | def get_html_price(self):
49 | price = self.get_price
50 | if price == self.sale_price:
51 | return "
%s %s
" %(self.sale_price, self.price)
52 | else:
53 | return "%s
" %(self.price)
54 |
55 |
56 | def create_slug(instance, new_slug=None):
57 | slug = slugify(instance.title)
58 | if new_slug is not None:
59 | slug = new_slug
60 | qs = Product.objects.filter(slug=slug)
61 | exists = qs.exists()
62 | if exists:
63 | new_slug = "%s-%s" %(slug, qs.first().id)
64 | return create_slug(instance, new_slug=new_slug)
65 | return slug
66 |
67 | def product_pre_save_receiver(sender, instance, *args, **kwargs):
68 | if not instance.slug:
69 | instance.slug = create_slug(instance)
70 |
71 |
72 | pre_save.connect(product_pre_save_receiver, sender=Product)
73 |
74 |
75 |
76 | def thumbnail_location(instance, filename):
77 | return "%s/%s" %(instance.product.slug, filename)
78 |
79 | THUMB_CHOICES = (
80 | ("hd", "HD"),
81 | ("sd", "SD"),
82 | ("micro", "Micro"),
83 | )
84 | class Thumbnail(models.Model):
85 | product = models.ForeignKey(Product)
86 | type = models.CharField(max_length=20, choices=THUMB_CHOICES, default='hd')
87 | height = models.CharField(max_length=20, null=True, blank=True)
88 | width = models.CharField(max_length=20, null=True, blank=True)
89 | media = models.ImageField(
90 | width_field= "width",
91 | height_field= "height",
92 | blank=True,
93 | null=True,
94 | upload_to=thumbnail_location)
95 |
96 | def __unicode__(self):
97 | return str(self.media.path)
98 |
99 | import os
100 | import shutil
101 | from PIL import Image
102 | import random
103 |
104 | from django.core.files import File
105 |
106 | def create_new_thumb(media_path, instance, owner_slug, max_length, max_width):
107 | filename = os.path.basename(media_path)
108 | thumb = Image.open(media_path)
109 | size = (max_length, max_width)
110 | thumb.thumbnail(size, Image.ANTIALIAS)
111 |
112 | temp_loc = "%s/%s/tmp" %(settings.MEDIA_ROOT, owner_slug)
113 |
114 | if not os.path.exists(temp_loc):
115 | os.makedirs(temp_loc)
116 |
117 | temp_file_path = os.path.join(temp_loc, filename)
118 | if os.path.exists(temp_file_path):
119 | temp_path = os.path.join(temp_loc, "%s" %(random.random()))
120 | os.makedirs(temp_path)
121 | temp_file_path = os.path.join(temp_path, filename)
122 |
123 | temp_image = open(temp_file_path, "w")
124 | thumb.save(temp_image)
125 | thumb_data = open(temp_file_path, "r")
126 |
127 | thumb_file = File(thumb_data)
128 | instance.media.save(filename, thumb_file)
129 | shutil.rmtree(temp_loc, ignore_errors=True)
130 | return True
131 |
132 |
133 | def product_post_save_receiver(sender, instance, created, *args, **kwargs):
134 | if instance.media:
135 | hd, hd_created = Thumbnail.objects.get_or_create(product=instance, type='hd')
136 | sd, sd_created = Thumbnail.objects.get_or_create(product=instance, type='sd')
137 | micro, micro_created = Thumbnail.objects.get_or_create(product=instance, type='micro')
138 |
139 | hd_max = (500, 500)
140 | sd_max = (350, 350)
141 | micro_max = (150, 150)
142 |
143 | media_path = instance.media.path
144 | owner_slug = instance.slug
145 | if hd_created:
146 | create_new_thumb(media_path, hd, owner_slug, hd_max[0], hd_max[1])
147 |
148 |
149 | if sd_created:
150 | create_new_thumb(media_path, sd, owner_slug, sd_max[0], sd_max[1])
151 |
152 |
153 | if micro_created:
154 | create_new_thumb(media_path, micro, owner_slug, micro_max[0], micro_max[1])
155 |
156 |
157 | post_save.connect(product_post_save_receiver, sender=Product)
158 |
159 |
160 | class MyProducts(models.Model):
161 | user = models.OneToOneField(settings.AUTH_USER_MODEL)
162 | products = models.ManyToManyField(Product, blank=True)
163 |
164 | def __unicode__(self):
165 | return "%s" %(self.products.count())
166 |
167 | class Meta:
168 | verbose_name = "My products"
169 | verbose_name_plural = "My Products"
170 |
171 | class ProductRating(models.Model):
172 | user = models.ForeignKey(settings.AUTH_USER_MODEL)
173 | product = models.ForeignKey(Product)
174 | rating = models.IntegerField(null=True, blank=True)
175 | verified = models.BooleanField(default=False)
176 |
177 | def __unicode__(self):
178 | return "%s" %(self.rating)
179 |
180 | class CuratedProducts(models.Model):
181 | user = models.ForeignKey(settings.AUTH_USER_MODEL)
182 | section_name = models.CharField(max_length=120)
183 | products = models.ManyToManyField(Product, blank=True)
184 | active = models.BooleanField(default=True)
185 |
186 | def __unicode__(self):
187 | return self.section_name
188 |
--------------------------------------------------------------------------------
/products/templatetags/__init__.py:
--------------------------------------------------------------------------------
1 | __author__ = 'NESCODE'
2 |
--------------------------------------------------------------------------------
/products/templatetags/get_thumbnail.py:
--------------------------------------------------------------------------------
1 | __author__ = 'NESCODE'
2 |
3 | from django import template
4 |
5 | register = template.Library()
6 |
7 | from ..models import Product, THUMB_CHOICES
8 |
9 | @register.filter
10 | def get_thumbnail(obj, arg):
11 | """
12 | obj == Product instance
13 |
14 | """
15 | arg = arg.lower()
16 | if not isinstance(obj, Product):
17 | raise TypeError("This is not a valid product model.")
18 |
19 | choice = dict(THUMB_CHOICES)
20 | if not choice.get(arg):
21 | raise TypeError("This is not a valid type for model.")
22 | try:
23 | return obj.thumbnail_set.filter(type=arg).first().media.url
24 | except:
25 | return None
26 |
--------------------------------------------------------------------------------
/products/tests.py:
--------------------------------------------------------------------------------
1 | from django.test import TestCase
2 |
3 | # Create your tests here.
4 |
--------------------------------------------------------------------------------
/products/urls.py:
--------------------------------------------------------------------------------
1 |
2 | from django.conf.urls import include, url
3 | from django.contrib import admin
4 | from django.views.generic.base import RedirectView
5 |
6 | from .views import (
7 | ProductCreateView,
8 | ProductDetailView,
9 | ProductDownloadView,
10 | ProductListView,
11 | ProductUpdateView,
12 | ProductRatingAjaxView,
13 | VendorListView,
14 | )
15 |
16 | urlpatterns = [
17 | url(r'^$', ProductListView.as_view(), name='list'),
18 | url(r'^vendor/$', RedirectView.as_view(pattern_name='products:list'), name='vendor_list'),
19 | url(r'^vendor/(?P[\w.@+-]+)/$', VendorListView.as_view(), name='vendor_detail'),
20 | url(r'^(?P\d+)/$', ProductDetailView.as_view(), name='detail'),
21 | url(r'^(?P[\w-]+)/$', ProductDetailView.as_view(), name='detail_slug'),
22 | url(r'^(?P\d+)/download/$', ProductDownloadView.as_view(), name='download'),
23 | url(r'^(?P[\w-]+)/download/$', ProductDownloadView.as_view(), name='download_slug'),
24 | url(r'^(?P\d+)/edit/$', ProductUpdateView.as_view(), name='update'),
25 | url(r'^(?P[\w-]+)/edit/$', ProductUpdateView.as_view(), name='update_slug'),
26 | url(r'^ajax/rating/$', ProductRatingAjaxView.as_view(), name='ajax_rating'),
27 | url(r'^vendor/$', VendorListView.as_view(), name='vendor_list'),
28 | ]
29 |
--------------------------------------------------------------------------------
/products/views.py:
--------------------------------------------------------------------------------
1 | import os
2 | from mimetypes import guess_type
3 | from django.conf import settings
4 | from django.core.servers.basehttp import FileWrapper
5 | from django.core.urlresolvers import reverse
6 | from django.db.models import Q, Avg, Count
7 | from django.http import Http404, HttpResponse, JsonResponse
8 | from django.shortcuts import render, get_object_or_404
9 | from django.views.generic.detail import DetailView
10 | from django.views.generic.edit import CreateView, UpdateView
11 | from django.views.generic.list import ListView
12 | from django.views.generic import View
13 |
14 | from analytics.models import TagView
15 | from djangodm.mixins import (
16 | LoginRequiredMixin,
17 | MultiSlugMixin,
18 | SubmitBtnMixin,
19 | AjaxRequiredMixin,
20 | )
21 |
22 | from tags.models import Tag
23 | from sellers.models import SellerAccount
24 | from sellers.mixins import SellerAccountMixin
25 | from .forms import ProductAddForm, ProductModelForm
26 | from .mixins import ProductManagerMixin
27 | from .models import Product, ProductRating, MyProducts
28 |
29 | class ProductRatingAjaxView(AjaxRequiredMixin, View):
30 | def post(self, request, *args, **kwargs):
31 | # if request.is_ajax():
32 | # raise Http404
33 | if not request.user.is_authenticated():
34 | return JsonResponse({}, status=401)
35 | # credit card required **
36 |
37 | user = request.user
38 | product_id = request.POST.get("product_id")
39 | rating_value = request.POST.get("rating_value")
40 | exists = Product.objects.filter(id=product_id).exists()
41 | if not exists:
42 | return JsonResponse({}, status=404)
43 |
44 | try:
45 | product_obj = Product.object.get(id=product_id)
46 | except:
47 | product_obj = Product.objects.filter(id=product_id).first()
48 |
49 | rating_obj, rating_obj_created = ProductRating.objects.get_or_create(
50 | user=user,
51 | product = product_obj
52 | )
53 |
54 | try:
55 | rating_obj = ProductRating.objects.get(user=user, product=product_obj)
56 | except ProductRating.MultipleObjectsReturned:
57 | rating_obj = ProductRating.objects.filter(user=user, product=product_obj).first()
58 | except:
59 | rating_obj = ProductRating()
60 | rating_obj.user = user
61 | rating_obj.product = product_obj
62 | rating_obj.rating = int(rating_value)
63 | myproducts = user.myproducts.products.all()
64 | if product_obj in myproducts:
65 | rating_obj.verified = True
66 | rating_obj.save()
67 |
68 | data = {
69 | "success": True
70 | }
71 | return JsonResponse(data)
72 |
73 | class ProductCreateView(SellerAccountMixin, SubmitBtnMixin, CreateView):
74 | model = Product
75 | template_name = "form.html"
76 | form_class = ProductModelForm
77 | submit_btn = "Add Product"
78 |
79 | def form_valid(self, form):
80 | seller = self.get_account()
81 | form.instance.seller = seller
82 | valid_data = super(ProductCreateView, self).form_valid(form)
83 | tags = form.cleaned_data.get("tags")
84 | if tags:
85 | tags_list = tags.split(",")
86 |
87 | for tag in tags_list:
88 | if not tag == " ":
89 | new_tag = Tag.objects.get_or_create(title=str(tag).strip())[0]
90 | new_tag.products.add(form.instance)
91 |
92 |
93 | return valid_data
94 |
95 | class ProductUpdateView(ProductManagerMixin, SubmitBtnMixin, MultiSlugMixin, UpdateView):
96 | model = Product
97 | template_name = "form.html"
98 | form_class = ProductModelForm
99 | submit_btn = "Update Product"
100 |
101 | def get_initial(self):
102 | initial = super(ProductUpdateView, self).get_initial()
103 | print initial
104 | tags = self.get_object().tag_set.all()
105 | initial["tags"] = ", ".join([x.title for x in tags])
106 | """
107 | tag_list = []
108 | for x in tags:
109 | tag_list.append(x.title)
110 | """
111 | return initial
112 |
113 | def form_valid(self, form):
114 | valid_data = super(ProductUpdateView, self).form_valid(form)
115 | tags = form.cleaned_data.get("tags")
116 | obj = self.get_object()
117 | obj.tag_set.clear()
118 | if tags:
119 | tags_list = tags.split(",")
120 |
121 | for tag in tags_list:
122 | if not tag == " ":
123 | new_tag = Tag.objects.get_or_create(title=str(tag).strip())[0]
124 | new_tag.products.add(self.get_object())
125 | return valid_data
126 |
127 |
128 | class ProductDetailView(MultiSlugMixin, DetailView):
129 | model = Product
130 |
131 | def get_context_data(self, *args, **kwargs):
132 | context = super(ProductDetailView, self).get_context_data(*args, **kwargs)
133 | obj = self.get_object()
134 | tags = obj.tag_set.all()
135 | rating_avg = obj.productrating_set.aggregate(Avg("rating"), Count("rating"))
136 | context["rating_avg"] = rating_avg
137 | if self.request.user.is_authenticated():
138 | rating_obj = ProductRating.objects.filter(user=self.request.user, product=obj)
139 | if rating_obj.exists():
140 | context['my_rating'] = rating_obj.first().rating
141 | for tag in tags:
142 | new_view = TagView.objects.add_count(self.request.user.id, tag)
143 | return context
144 |
145 | class ProductDownloadView(MultiSlugMixin, DetailView):
146 | model = Product
147 |
148 | def get(self, request, *args, **kwargs):
149 | obj = self.get_object()
150 | if obj in request.user.myproducts.products.all():
151 | filepath = os.path.join(settings.PROTECTED_ROOT, obj.media.path)
152 | guessed_type = guess_type(filepath)[0]
153 | wrapper = FileWrapper(file(filepath))
154 | mimetype = 'application/force-download'
155 | if guessed_type:
156 | mimetype = guessed_type
157 | response = HttpResponse(wrapper, content_type='mimetype')
158 |
159 | if not request.GET.get("preview"):
160 | response["Content-Disposition"] = "attachment; filename=%s" %(obj.media.name)
161 |
162 | response["X-SendFile"] = str(obj.media.name)
163 | return response
164 | else:
165 | raise Http404
166 |
167 | class SellerProductListView(SellerAccountMixin, ListView):
168 | model = Product
169 | template_name = "sellers/product_list_view.html"
170 |
171 | def get_queryset(self, *args, **kwargs):
172 | qs = super(SellerProductListView, self).get_queryset(**kwargs)
173 | qs = qs.filter(seller=self.get_account())
174 | query = self.request.GET.get("q")
175 | if query:
176 | qs = qs.filter(
177 | Q(title__icontains=query)|
178 | Q(description__icontains=query)
179 | ).order_by("title")
180 | return qs
181 |
182 | class VendorListView(ListView):
183 | model = Product
184 | template_name = "products/product_list.html"
185 |
186 | def get_object(self):
187 | username = self.kwargs.get("vendor_name")
188 | seller = get_object_or_404(SellerAccount, user__name=username)
189 | return seller
190 |
191 | def get_context_data(self, *args, **kwargs):
192 | context = super(VendorListView, self).get_context_data(*args, **kwargs)
193 | context["vendor_name"] = str(self.get_object().user.username)
194 | return context
195 |
196 | def get_queryset(self, *args, **kwargs):
197 | username = self.kwargs.get("vendor_name")
198 | seller = get_object_or_404(SellerAccount, user__name=username)
199 | qs = super(VendorListView, self).get_queryset(**kwargs).filter(seller=seller)
200 | query = self.request.GET.get("q")
201 | if query:
202 | qs = qs.filter(
203 | Q(title__icontains=query)|
204 | Q(description__icontains=query)
205 | ).order_by("title")
206 | return qs
207 |
208 | class ProductListView(ListView):
209 | model = Product
210 |
211 | def get_queryset(self, *args, **kwargs):
212 | qs = super(ProductListView, self).get_queryset(**kwargs)
213 | query = self.request.GET.get("q")
214 | if query:
215 | qs = qs.filter(
216 | Q(title__icontains=query)|
217 | Q(description__icontains=query)
218 | ).order_by("title")
219 | return qs
220 |
221 | class UserLibraryListView(LoginRequiredMixin, ListView):
222 | model = MyProducts
223 | template_name = "products/library_list.html"
224 |
225 | def get_queryset(self, *args, **kwargs):
226 | obj = MyProducts.objects.get_or_create(user=self.request.user)[0]
227 | qs = obj.products.all()
228 | query = self.request.GET.get("q")
229 | if query:
230 | qs = qs.filter(
231 | Q(title__icontains=query)|
232 | Q(description__icontains=query)
233 | ).order_by("title")
234 | return qs
235 |
236 | def create_view(request):
237 | # Form
238 | form = ProductModelForm(request.POST or None, request.FILES or None)
239 | if form.is_valid():
240 | print form.cleaned_data.get("publish")
241 | instance = form.save(commit=False)
242 | instance.sale_price = instance.price
243 | instance.save()
244 | template = "form.html"
245 | context = {
246 | "form": form,
247 | "submit_btn": "Create Product"
248 | }
249 | return render(request, template, context)
250 |
251 | def update_view(request, object_id=None):
252 | product = get_object_or_404(Product, id=object_id)
253 | forms = ProductModelForm(request.POST or None, instance=product)
254 | if forms.is_valid():
255 | instance = forms.save(commit=False)
256 | # instance.sale_price = instance.price
257 | instance.save()
258 | template = "form.html"
259 | context = {
260 | "object": product,
261 | "form": forms,
262 | "submit_btn": "Update Product"
263 | }
264 | return render(request, template, context)
265 |
266 | def detail_slug_view(request, slug=None):
267 | product = Product.objects.get(slug=slug)
268 | try:
269 | product = get_object_or_404(Product, slug=slug)
270 | except Product.MultipleObjectsReturned:
271 | product = Product.objects.filter(slug=slug).order_by("-title").first()
272 | template = "detail_view.html"
273 | context = {
274 | "object": product
275 | }
276 | return render(request, template, context)
277 |
278 | def detail_view(request, object_id=None):
279 | product = get_object_or_404(Product, id=object_id)
280 | template = "detail_view.html"
281 | context = {
282 | "object": product
283 | }
284 | return render(request, template, context)
285 |
286 | def list_view(request):
287 | print request
288 | queryset = Product.objects.all()
289 | template = "list_view.html"
290 | context = {
291 | "queryset": queryset
292 | }
293 | return render(request, template, context)
294 |
--------------------------------------------------------------------------------
/requirement.txt:
--------------------------------------------------------------------------------
1 | Django==1.8.7
2 | Pillow==3.0.0
3 | psycopg2==2.6.1
4 | wheel==0.24.0
5 |
--------------------------------------------------------------------------------
/sellers/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nescode/djangodm/6bc883169f34e693c948487807de2743a445978e/sellers/__init__.py
--------------------------------------------------------------------------------
/sellers/admin.py:
--------------------------------------------------------------------------------
1 | from django.contrib import admin
2 |
3 |
4 | # Seller module registration in Django admin
5 |
6 | from .models import SellerAccount
7 |
8 | admin.site.register(SellerAccount)
9 |
--------------------------------------------------------------------------------
/sellers/forms.py:
--------------------------------------------------------------------------------
1 | from django import forms
2 |
3 | # New seller forms
4 |
5 | class NewSellerForm(forms.Form):
6 | agree = forms.BooleanField(label='Agree to Terms', widget=forms.CheckboxInput)
7 |
--------------------------------------------------------------------------------
/sellers/migrations/0001_initial.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | from __future__ import unicode_literals
3 |
4 | from django.db import migrations, models
5 | from django.conf import settings
6 |
7 |
8 | class Migration(migrations.Migration):
9 |
10 | dependencies = [
11 | migrations.swappable_dependency(settings.AUTH_USER_MODEL),
12 | ]
13 |
14 | operations = [
15 | migrations.CreateModel(
16 | name='SellerAccount',
17 | fields=[
18 | ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
19 | ('active', models.BooleanField(default=False)),
20 | ('timestamp', models.DateTimeField(auto_now_add=True)),
21 | ('managers', models.ManyToManyField(related_name='manager_sellers', to=settings.AUTH_USER_MODEL, blank=True)),
22 | ('user', models.ForeignKey(to=settings.AUTH_USER_MODEL)),
23 | ],
24 | ),
25 | ]
26 |
--------------------------------------------------------------------------------
/sellers/migrations/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nescode/djangodm/6bc883169f34e693c948487807de2743a445978e/sellers/migrations/__init__.py
--------------------------------------------------------------------------------
/sellers/mixins.py:
--------------------------------------------------------------------------------
1 | import datetime
2 | from django.db.models import Count, Min, Sum, Avg, Max
3 | from billing.models import Transaction
4 | from djangodm.mixins import LoginRequiredMixin
5 | from products.models import Product
6 |
7 | from .models import SellerAccount
8 |
9 |
10 | # Seller account mixins
11 |
12 | class SellerAccountMixin(LoginRequiredMixin, object):
13 | account = None
14 | products = []
15 | transactions = []
16 |
17 | def get_account(self):
18 | user = self.request.user
19 | accounts = SellerAccount.objects.filter(user=user)
20 | if accounts.exists() and accounts.count() == 1:
21 | self.account = accounts.first()
22 | return accounts.first()
23 | return None
24 |
25 | def get_products(self):
26 | account = self.get_account()
27 | products = Product.objects.filter(seller=account)
28 | self.products = products
29 | return products
30 |
31 | def get_transactions(self):
32 | products = self.get_products()
33 | transactions = Transaction.objects.filter(product__in=products)
34 | return transactions
35 |
36 | def get_transactions_today(self):
37 | today = datetime.date.today()
38 | today_min = datetime.datetime.combine(today, datetime.time.min)
39 | today_max = datetime.datetime.combine(today, datetime.time.max)
40 | # print today, today_min, today_max
41 | return self.get_transactions().filter(timestamp__range=(today_min, today_max))
42 |
43 | def get_total_sales(self):
44 | transactions = self.get_transactions().aggregate(Sum("price"))
45 | total_sales = transactions["price__sum"]
46 | return total_sales
47 |
48 | def get_today_sales(self):
49 | transactions = self.get_transactions_today().aggregate(Sum("price"), Avg("price"))
50 | total_sales = transactions["price__sum"]
51 | return total_sales
52 |
--------------------------------------------------------------------------------
/sellers/models.py:
--------------------------------------------------------------------------------
1 | from django.conf import settings
2 | from django.db import models
3 | from django.core.urlresolvers import reverse
4 |
5 | # Seller account
6 |
7 | class SellerAccount(models.Model):
8 | user = models.ForeignKey(settings.AUTH_USER_MODEL)
9 | managers = models.ManyToManyField(
10 | settings.AUTH_USER_MODEL,
11 | related_name="manager_sellers",
12 | blank=True
13 | )
14 | active = models.BooleanField(default=False)
15 | timestamp = models.DateTimeField(auto_now_add=True, auto_now=False)
16 |
17 | def __unicode__(self):
18 | return str(self.user.username)
19 |
20 | def get_absolute_url(self):
21 | return reverse("products:vendor_detail", kwargs={"vendor_name": self.user.username})
22 |
--------------------------------------------------------------------------------
/sellers/tests.py:
--------------------------------------------------------------------------------
1 | from django.test import TestCase
2 |
3 | # Create your tests here.
4 |
--------------------------------------------------------------------------------
/sellers/urls.py:
--------------------------------------------------------------------------------
1 |
2 | from django.conf.urls import include, url
3 | from django.contrib import admin
4 |
5 | from products.views import (
6 | ProductCreateView,
7 | SellerProductListView,
8 | ProductUpdateView,
9 | )
10 |
11 | # Import views from seller app to process url
12 |
13 | from .views import (
14 | SellerDashboard,
15 | SellerTransactionListView,
16 | SellerProductDetailRedirectView,
17 | )
18 |
19 | # Url setup for seller
20 |
21 | urlpatterns = [
22 | url(r'^$', SellerDashboard.as_view(), name='dashboard'),
23 | url(r'^transactions/$', SellerTransactionListView.as_view(), name='transactions'),
24 | url(r'^products/$', SellerProductListView.as_view(), name='product_list'),
25 | url(r'^products/(?P\d+)/$', SellerProductDetailRedirectView.as_view()),
26 | url(r'^products/(?P\d+)/edit/$', ProductUpdateView.as_view(), name='product_edit'),
27 | url(r'^products/add/$', ProductCreateView.as_view(), name='product_create'),
28 | ]
29 |
--------------------------------------------------------------------------------
/sellers/views.py:
--------------------------------------------------------------------------------
1 | from django.shortcuts import render
2 | from django.views.generic import View
3 | from django.views.generic.base import RedirectView
4 | from django.views.generic.edit import FormMixin
5 | from django.views.generic.list import ListView
6 | from django.shortcuts import get_object_or_404
7 |
8 | from billing.models import Transaction
9 | from djangodm.mixins import LoginRequiredMixin
10 | from products.models import Product
11 |
12 | from .forms import NewSellerForm
13 | from .mixins import SellerAccountMixin
14 | from .models import SellerAccount
15 |
16 | # Seller product list and details
17 |
18 | class SellerProductDetailRedirectView(RedirectView):
19 | permanent = True
20 |
21 | def get_redirect_url(self, *args, **kwargs):
22 | obj = get_object_or_404(Product, pk=kwargs['pk'])
23 | return obj.get_absolute_url()
24 |
25 | # Seller transaction lists
26 |
27 | class SellerTransactionListView(SellerAccountMixin, ListView):
28 | model = Transaction
29 | template_name = "sellers/transaction_list_view.html"
30 |
31 | def get_queryset(self):
32 | return self.get_transactions()
33 |
34 | # Seller dashboard
35 |
36 | class SellerDashboard(SellerAccountMixin, FormMixin, View):
37 | form_class = NewSellerForm
38 | success_url = "/seller/"
39 |
40 | def post(self, request, *args, **kwargs):
41 | form = self.get_form()
42 | if form.is_valid():
43 | return self.form_valid(form)
44 | else:
45 | return self.form_invalid(form)
46 |
47 | def get(self, request, *args, **kwargs):
48 | apply_form = self.get_form() #NewSellerForm()
49 | account = self.get_account()
50 | exists = account
51 | active = None
52 | context = {}
53 | if exists:
54 | active = account.active
55 | if not exists and not active:
56 | context["title"] = "Apply for Account"
57 | context["apply_form"] = apply_form
58 | elif exists and not active:
59 | context["title"] = "Account Pending"
60 | elif exists and active:
61 | context["title"] = "Seller Dashboard"
62 | context["products"] = self.get_products()
63 | transactions_today = self.get_transactions_today()
64 | context["transactions_today"] = transactions_today
65 | context["today_sales"] = self.get_today_sales()
66 | context["total_sales"] = self.get_total_sales()
67 | context["transactions"] = self.get_transactions().exclude(pk__in=transactions_today)[:5]
68 | else:
69 | pass
70 |
71 | return render(request, "sellers/dashboard.html", context)
72 |
73 | def form_valid(self, form):
74 | valid_data = super(SellerDashboard, self).form_valid(form)
75 | obj = SellerAccount.objects.create(user=self.request.user)
76 | return valid_data
77 |
--------------------------------------------------------------------------------
/tags/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nescode/djangodm/6bc883169f34e693c948487807de2743a445978e/tags/__init__.py
--------------------------------------------------------------------------------
/tags/admin.py:
--------------------------------------------------------------------------------
1 | from django.contrib import admin
2 |
3 | from .models import Tag
4 |
5 | admin.site.register(Tag)
6 |
--------------------------------------------------------------------------------
/tags/migrations/0001_initial.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | from __future__ import unicode_literals
3 |
4 | from django.db import migrations, models
5 |
6 |
7 | class Migration(migrations.Migration):
8 |
9 | dependencies = [
10 | ('products', '0014_remove_thumbnail_user'),
11 | ]
12 |
13 | operations = [
14 | migrations.CreateModel(
15 | name='Tag',
16 | fields=[
17 | ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
18 | ('title', models.CharField(unique=True, max_length=120)),
19 | ('slug', models.SlugField(unique=True)),
20 | ('active', models.BooleanField(default=True)),
21 | ('products', models.ManyToManyField(to='products.Product', blank=True)),
22 | ],
23 | ),
24 | ]
25 |
--------------------------------------------------------------------------------
/tags/migrations/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nescode/djangodm/6bc883169f34e693c948487807de2743a445978e/tags/migrations/__init__.py
--------------------------------------------------------------------------------
/tags/models.py:
--------------------------------------------------------------------------------
1 | from django.db import models
2 | from django.db.models.signals import pre_save, post_save
3 | from django.core.urlresolvers import reverse
4 | from django.utils.text import slugify
5 |
6 | from products.models import Product
7 |
8 | class TagQuerySet(models.query.QuerySet):
9 | def active(self):
10 | return self.filter(active=True)
11 |
12 | class TagManager(models.Manager):
13 | def get_queryset(self):
14 | return TagQuerySet(self.model, using=self._db)
15 |
16 | def all(self, *args, **kwargs):
17 | return super(TagManager, self).all(*args, **kwargs).active()
18 |
19 | # def active(self, *args, **kwargs):
20 | # return self.get_queryset().filter(active=True)
21 |
22 | class Tag(models.Model):
23 | title = models.CharField(max_length=120, unique=True)
24 | slug = models.SlugField(unique=True)
25 | products = models.ManyToManyField(Product, blank=True)
26 | active = models.BooleanField(default=True)
27 |
28 | objects = TagManager()
29 |
30 | def __unicode__(self):
31 | return str(self.title)
32 |
33 | def get_absolute_url(self):
34 | view_name = "tags:detail"
35 | return reverse(view_name, kwargs={"slug": self.slug})
36 |
37 | def tag_pre_save_receiver(sender, instance, *args, **kwargs):
38 | if not instance.slug:
39 | instance.slug = slugify(instance.title)
40 |
41 | pre_save.connect(tag_pre_save_receiver, sender=Tag)
42 |
--------------------------------------------------------------------------------
/tags/tests.py:
--------------------------------------------------------------------------------
1 | from django.test import TestCase
2 |
3 | # Create your tests here.
4 |
--------------------------------------------------------------------------------
/tags/urls.py:
--------------------------------------------------------------------------------
1 | from django.conf.urls import include, url
2 | from django.contrib import admin
3 |
4 | from .views import (
5 | TagDetailView,
6 | TagListView,
7 | )
8 |
9 | urlpatterns = [
10 | url(r'^$', TagListView.as_view(), name='list'),
11 | url(r'^(?P[\w-]+)/$', TagDetailView.as_view(), name='detail'),
12 | ]
13 |
--------------------------------------------------------------------------------
/tags/views.py:
--------------------------------------------------------------------------------
1 | from django.shortcuts import render
2 |
3 | from django.views.generic.detail import DetailView
4 | from django.views.generic.list import ListView
5 |
6 | from analytics.models import TagView
7 | from .models import Tag
8 |
9 | class TagDetailView(DetailView):
10 | model = Tag
11 |
12 | def get_context_data(self, *args, **kwargs):
13 | context = super(TagDetailView, self).get_context_data(*args, **kwargs)
14 | if self.request.user.is_authenticated():
15 | tag = self.get_object()
16 | new_view = TagView.objects.add_count(self.request.user, tag)
17 | return context
18 |
19 |
20 | class TagListView(ListView):
21 | model = Tag
22 |
23 | def get_queryset(self):
24 | return Tag.objects.all()
25 |
--------------------------------------------------------------------------------
/templates/base.html:
--------------------------------------------------------------------------------
1 | {% load staticfiles %}
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 | Django Digital Marketplace
10 |
11 |
12 |
13 |
73 |
74 |
75 |
76 |
87 |
88 | {% block content %}
89 | {% endblock content %}
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
106 |
107 |
108 |
--------------------------------------------------------------------------------
/templates/checkout/test.html:
--------------------------------------------------------------------------------
1 | {% extends "base.html" %}
2 |
3 | csrfmiddlewaretoken: "{{ csrf_token }}",
4 |
5 | success: function(data, textStatus, jqXHR){
6 | console.log(data.works)
7 | if (data.works) {
8 | $("#content").html(data.time)
9 | }
10 | // console.log(textStatus)
11 | // console.log(jqXHR)
12 | },
13 | error: function(jqXHR, textStatus, errorThrown){
14 | // console.log(errorThrown)
15 | // console.log(textStatus)
16 | // alert(errorThrown)
17 | if (jqXHR.status == 404) {
18 | alert("Page not found!")
19 | }
20 | console.log(jqXHR.status)
21 | }
22 |
23 |
55 |
56 |
57 |
58 |
59 |
60 | {% block content %}
61 |
62 |
63 | Test Ajax
64 |
65 |
66 |
67 | {% endblock %}
68 |
--------------------------------------------------------------------------------
/templates/create_view.html:
--------------------------------------------------------------------------------
1 | {% extends 'base.html' %}
2 |
3 | {% block content %}
4 |
5 | {% include "form_include.html" with form=form submit_btn="Create Product" %}
6 | {% endblock content %}
7 |
--------------------------------------------------------------------------------
/templates/dashboard/view.html:
--------------------------------------------------------------------------------
1 | {% extends "base.html" %}
2 |
3 | {% block content %}
4 |
5 |
Suggested Products
6 | {% include "products/product_list_snippet.html" with product_list=products %}
7 |
8 |
9 |
10 |
11 |
Top Tags
12 | {% for tag in top_tags %}
13 |
{{ tag }}
14 | {% endfor %}
15 |
16 |
17 | {% if curated.count > 0 %}
18 | {% for curated_section in curated %}
19 |
20 |
{{ curated_section.section_name }}
21 | {% include "products/product_list_snippet.html" with product_list=curated_section.products.all %}
22 |
23 | {% endfor %}
24 | {% endif %}
25 |
26 | {% endblock content %}
27 |
--------------------------------------------------------------------------------
/templates/detail_view.html:
--------------------------------------------------------------------------------
1 | {% extends 'base.html' %}
2 |
3 | {% block content %}
4 |
5 | Product: {{ object.title }}
6 | {{ object.description }}
7 | {{ object.price }}
8 | {% endblock content %}
9 |
--------------------------------------------------------------------------------
/templates/form.html:
--------------------------------------------------------------------------------
1 | {% extends 'base.html' %}
2 |
3 | {% block content %}
4 |
5 | {% include "form_include.html" with form=form submit_btn=submit_btn %}
6 | {% endblock content %}
7 |
--------------------------------------------------------------------------------
/templates/form_include.html:
--------------------------------------------------------------------------------
1 |
6 |
--------------------------------------------------------------------------------
/templates/list_view.html:
--------------------------------------------------------------------------------
1 | {% extends 'base.html' %}
2 | {% block style %}
3 | h1{
4 | color: #007CAE;
5 | }
6 | {% endblock style %}
7 | {% block content %}
8 | List list
9 |
10 | {{ queryset }}
11 | {% endblock content %}
12 |
--------------------------------------------------------------------------------
/templates/products/library_list.html:
--------------------------------------------------------------------------------
1 | {% extends "base.html" %}
2 | {% load get_thumbnail %}
3 |
4 | {% block style %}
5 |
6 | {% endblock style %}
7 |
8 |
9 | {% block content %}
10 | Library
11 |
12 |
16 |
17 |
18 |
19 |
20 |
21 | Product |
22 | Image |
23 | |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 | {% for instance in product_list %}
32 |
33 |
34 | {{ instance.title }}
35 | |
36 |
37 |
38 | {% if instance|get_thumbnail:"micro" %}
39 |
40 |
41 |
42 |
43 | {% endif %}
44 |
45 | |
46 |
47 |
48 | {% if instance.get_download %}
49 | Download
50 | {% else %}
51 | Coming Soon
52 | {% endif %}
53 | |
54 |
55 | {% empty %}
56 | No products in library yet, please shop now.
57 |
58 |
59 | {% endfor %}
60 |
61 |
62 |
63 |
64 |
65 | {% endblock %}
66 |
--------------------------------------------------------------------------------
/templates/products/product_detail.html:
--------------------------------------------------------------------------------
1 | {% extends "base.html" %}
2 |
3 |
56 |
57 |
58 |
59 |
148 |
149 |
150 |
151 |
152 | {% block content %}
153 |
154 |
155 |
156 |
{{ object.description }}
157 |
{{ object.get_html_price|safe }}
158 |
Purchases: {{ object.transaction_set.count }}
159 |
160 |
161 | {% for img in object.thumbnail_set.all %}
162 |

163 |
164 | {% endfor %}
165 |
166 |
167 |
168 |
169 |
170 |
204 |
205 |
206 |
207 |
208 | {% endblock content %}
209 |
--------------------------------------------------------------------------------
/templates/products/product_list.html:
--------------------------------------------------------------------------------
1 | {% extends "base.html" %}
2 |
3 | {% block style %}
4 |
5 | {% endblock style %}
6 | {% block content %}
7 | {% if vendor_name %}{{ vendor_name }}{% endif %} Products
8 |
9 |
13 |
14 |
15 |
16 | {% include "products/product_list_snippet.html" with product_list=object_list %}
17 |
18 |
19 |
20 | {% endblock %}
21 |
--------------------------------------------------------------------------------
/templates/products/product_list_snippet.html:
--------------------------------------------------------------------------------
1 | {% load get_thumbnail %}
2 |
3 | {% for instance in product_list %}
4 |
5 | {{ instance.title }}
6 | {% if instance|get_thumbnail:"micro" %}
7 |
8 | {% endif %}
9 |
10 | {{ instance.get_html_price|safe }}
11 | {% empty %}
12 | No Products yet.
13 | {% endfor %}
14 |
--------------------------------------------------------------------------------
/templates/sellers/dashboard.html:
--------------------------------------------------------------------------------
1 | {% extends "base.html" %}
2 |
3 | {% block content %}
4 | {{ title }}
5 |
6 |
7 |
8 |
9 | {% if apply_form %}
10 |
11 |
22 |
23 | {% endif %}
24 |
25 |
26 |
27 | {% if products %}
28 |
32 |
33 | {% endif %}
34 |
35 |
36 |
37 | {% if transactions %}
38 |
39 |
Today's sales are ${{ today_sales }}
40 | {% include "sellers/transaction_list.html" with transaction_list=transactions_today %}
41 |
42 |
43 |
Recent Transaction History (Total Sales: ${{ total_sales }})
44 | {% include "sellers/transaction_list.html" with transaction_list=transactions %}
45 |
46 |
47 |
View all transactions
48 |
49 |
50 | {% endif %}
51 |
52 | {% endblock %}
53 |
--------------------------------------------------------------------------------
/templates/sellers/product_list_view.html:
--------------------------------------------------------------------------------
1 | {% extends "base.html" %}
2 | {% load get_thumbnail %}
3 | {% block style %}
4 |
5 | {% endblock style %}
6 | {% block content %}
7 | Seller Products
8 |
9 |
13 |
14 |
15 |
16 | {% for instance in product_list %}
17 |
18 |
19 | {{ instance.id }}
20 | |
21 |
22 | {{ instance.title }} | |
23 |
24 | {% if instance|get_thumbnail:"micro" %}
25 |
26 | {% endif %}
27 | |
28 |
29 | {{ instance.price }} |
30 | Edit | Delete |
31 | {% empty %}
32 | No Products yet.
33 | {% endfor %}
34 |
35 |
36 |
37 |
38 | {% endblock %}
39 |
--------------------------------------------------------------------------------
/templates/sellers/transaction_list.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Product |
6 | User |
7 | Sale Total |
8 | |
9 |
10 |
11 | {% for trans in transaction_list %}
12 |
13 | {{ trans.product }} |
14 | {{ trans.user }} |
15 | {{ trans.price }} |
16 | {{ trans.timestamp|timesince }} ago |
17 |
18 | {% endfor %}
19 |
20 |
21 |
--------------------------------------------------------------------------------
/templates/sellers/transaction_list_view.html:
--------------------------------------------------------------------------------
1 | {% extends "base.html" %}
2 |
3 | {% block style %}
4 |
5 | {% endblock style %}
6 | {% block content %}
7 | Transactions
8 |
9 |
10 | {% include "sellers/transaction_list.html" with transaction_list=object_list %}
11 |
12 |
13 |
14 | {% endblock %}
15 |
--------------------------------------------------------------------------------
/templates/tags/tag_detail.html:
--------------------------------------------------------------------------------
1 | {% extends "base.html" %}
2 | {% load get_thumbnail %}
3 | {% block content %}
4 |
5 |
6 | {% for product_instance in object.products.all %}
7 |
8 | -
9 | {# {{ product_instance }}#}
10 | {# {{ product_instance.thumbnail_set.all }}#}
11 | {# {{ product_instance.thumbnail_set.all.first.media }}#}
12 |
14 |
15 |
16 | {% empty %}
17 | - No Tags yet.
18 | {% endfor %}
19 |
20 |
21 | {% endblock %}
22 |
--------------------------------------------------------------------------------
/templates/tags/tag_list.html:
--------------------------------------------------------------------------------
1 | {% extends "base.html" %}
2 |
3 | {% block style %}
4 |
5 | {% endblock style %}
6 | {% block content %}
7 | Tags
8 |
9 |
13 |
14 |
15 | {% for instance in object_list %}
16 | - {{ instance.title }}
17 | ({{ instance.products.count }} Product{% if instance.products.count > 1 %}s{% endif %})
18 |
19 | {% empty %}
20 | - No Tags yet.
21 | {% endfor %}
22 |
23 |
24 | {% endblock %}
25 |
--------------------------------------------------------------------------------
/templates/update_view.html:
--------------------------------------------------------------------------------
1 | {% extends 'base.html' %}
2 |
3 | {% block content %}
4 |
5 | {% include "form_include.html" with form=form submit_btn="Update Product" %}
6 | {% endblock content %}
7 |
--------------------------------------------------------------------------------