I have two models: a Project model and ProjectNotes. ProjectNotes are notes that are related to Projects. The ProjectNotes model has a project field that is a foreign key of the related Project.
The problem I have is that when I delete a note on a project, the entire project is deleted. Only the note should be deleted. I think I have on_delete=models.cascade set up correctly on the ProjectNotes model. So I think the issue is with the view.
The models:
class ProjectManager(models.Manager):
def search(self, query=None):
qs = self.get_queryset()
if query is not None:
or_lookup = (
Q(title__icontains=query) |
Q(description__icontains=query)
# Q(slug__icontains=query)
)
qs = qs.filter(or_lookup).distinct() # distinct() is often necessary with Q lookups
return qs
def get_with_counted_notes_documents_todos(self):
queryset = self.get_queryset().annotate(
num_notes=Count('notes'),
num_documents=Count('project_documents'),
num_todos=Count('todo_group')
)
return queryset
class Project(models.Model):
title = models.CharField(max_length= 200)
description = tinymce_models.HTMLField()
status = models.CharField(max_length=20, choices=PROJECT_CHOICES, default="active")
date = models.DateTimeField(auto_now_add=True, null=True)
created_by = models.ForeignKey(CustomUser, editable=False, null=True, blank=True, on_delete=models.RESTRICT)
tags = tagulous.models.TagField(to=SiteWideTags, blank=True, related_name='projects_tags')
objects = ProjectManager()
def __str__(self):
return self.title
def get_absolute_url(self):
return reverse('company_project:project_detail', args=[str(self.id)])
class ProjectNotesManager(models.Manager):
def search(self, query=None):
qs = self.get_queryset()
if query is not None:
or_lookup = (
Q(title__icontains=query) |
Q(body__icontains=query)
# Q(slug__icontains=query)
)
qs = qs.filter(or_lookup).distinct() # distinct() is often necessary with Q lookups
return qs
class ProjectNotes(models.Model):
title = models.CharField(max_length=200)
body = tinymce_models.HTMLField()
project = models.ForeignKey(Project, default=0, blank=True, on_delete=models.CASCADE, related_name='notes')
date = models.DateTimeField(auto_now_add=True, null=True)
created_by = models.ForeignKey(CustomUser, editable=False, null=True, blank=True, on_delete=models.RESTRICT)
def __str__(self):
return self.title
def get_absolute_url(self):
return reverse('company_project:project_note_detail', args=[self.project_id, str(self.id)])
objects = ProjectNotesManager()
The view:
class ProjectNoteDeleteView(DeleteBreadcrumbMixin, DeleteView):
model = ProjectNotes
template_name = 'company_accounts/delete_project_note.html'
#cached_property
def crumbs(self):
project = self.get_object()
relate_notes = self.get_note()
bread_crumbs = [
("projects", reverse(
"company_project:" + CompanyProjects.list_view_name, )
),
(f"{project.title}",
reverse(
"company_project:" + CompanyProjectsDetailView.detail_view_name,
kwargs={'pk': project.id})
),
("notes", reverse(
"company_project:projectnotes_list",
kwargs={'pk': project.id})
),
(f"{relate_notes.title}",
reverse(
"company_project:project_note_detail",
kwargs={'project_pk': project.id, 'pk': self.kwargs.get('pk')})
),
(f"Delete: {relate_notes.title}",
reverse(
"company_project:project_note_detail",
kwargs={'project_pk': project.id, 'pk': self.kwargs.get('pk')})
)
]
return bread_crumbs
def get_object(self):
return get_object_or_404(Project, id=self.kwargs.get('project_pk'))
def get_note(self):
project = self.get_object()
return get_object_or_404(project.notes, id=self.kwargs.get('pk'))
def related_project(self, **kwargs):
project = get_object_or_404(Project, id=self.kwargs.get('project_pk'))
return project
def get_context_data(self, **kwargs):
# Call the base implementation first to get a context
context = super().get_context_data(**kwargs)
context["note"] = self.get_note()
context['project'] = self.get_object()
return context
def get_success_url(self):
return reverse('company_project:project_detail', args=[(self.object.id)])
The URL:
path('project/<int:project_pk>/note/<int:pk>/delete', ProjectNoteDeleteView.as_view(), name='project_note_delete'),
The template:
<!-- templates/company_accounts/delete_project_note.html -->
{% extends 'base.html' %}
{% block content %}
<div class="section-container container">
<div class="general-main-column">
<div class="row">
<form method="post">{% csrf_token %}
<p>Are you sure you want to permanently delete project note "{{ note }}"?</p>
<input type="submit" class="btn btn-danger" value="Confirm">
</form>
</div>
</div>
</div>
{% endblock content %}
Look at the DeleteView implementation here.
As we can see DeleteView calls delete method for the result of calling get_object function, but your implementation of get_object return instance of Project model:
def get_object(self):
return get_object_or_404(Project, id=self.kwargs.get('project_pk'))
so this is a reason why your project deletes after you try to delete project note.
Related
I am currently building a website that will allow the sale of mixing and mastering services. As it is a small set of services, I don't need a shopping cart or any elaborate form of ordering. Instead, I would like a customer details page (which informs my 'Customer' model), an order page where the customer selects what exactly they will be purchasing and uploads any relelvent files (which also informs my 'Order' model), and finally sends the customer to a stripe checkout page.
Currently, the Custome rdetails form is up and running and saving the data to the appropriate database model. Once they click continue, I am struggling to understand how to store the primary key of the Customer instance the user created upon filling out the form, and saving this data in the next form through the foreign key relationship.
Similarly, before being sent to Stripe checkout, I would like to create an 'Order Review' page, reviewing the details of their order. I'm not sure how to pull the primary key of the Order intance that was just created in order to for a Model view on the subsequent page. I believe what I;m missing in order to achieve either of these things is how to get the primary key of the database intsance created by the customer upon submitting the form.
Here is the code relevant to my question, incase I am going about this fundamentally wrong:
models.py
class Customer(models.Model):
first_name = models.CharField(max_length=200, null=False)
last_name = models.CharField(max_length=200, null=False)
phone = models.CharField(max_length=10)
email = models.EmailField(null=False)
date_created = models.DateTimeField(auto_now_add=True)
def __str__(self):
return self.last_name
class Product(models.Model):
MIXMAS = 'Mixing and Mastering Package'
MASO = 'Mastering Only'
FEAT = 'Request a Feature'
TUT = 'Request a Tutor'
NONE = 'Select an option'
PRODUCT_NAME_CHOICES = [
(MIXMAS, 'Mixing and Mastering Package'),
(MASO, 'Mastering Only'),
(FEAT, 'Request a Feature'),
(TUT, 'Request a Tutor'),
(NONE, 'Select an option')
]
name = models.CharField(max_length=100, choices=PRODUCT_NAME_CHOICES, default=NONE)
stripe_product_id = models.CharField(max_length=100)
product_description = models.CharField(max_length=300, null=True)
def __str__(self):
return self.name
class Price(models.Model):
product = models.ForeignKey(Product, on_delete=models.CASCADE, related_name="prices")
stripe_price_id = models.CharField(max_length=100)
price = models.IntegerField(default=0) # cents
price_description = models.CharField(max_length=300, null=True)
class Meta:
ordering = ['price']
def get_display_price(self):
return "{0:.2f}".format(self.price / 100)
def __str__(self):
return '%s %s %s %s' % ("$", self.price, "-", self.price_description)
class Order(models.Model):
product = models.ForeignKey(Product, on_delete=models.CASCADE, verbose_name='Package Type: ')
price = models.ForeignKey(Price, on_delete=models.CASCADE, verbose_name="Number of stems: ")
cust_requests = models.TextField(max_length=500, null=True, verbose_name='Enter any specific requests here: (Leave blank if none): ')
reference_track = models.CharField(max_length=200, null=True, verbose_name='Reference Track (Leave blank if none): ')
music_file = models.FileField(upload_to='studio_orders/', verbose_name="Upload zipped music file: ")
customer = models.ForeignKey(Customer, on_delete=models.CASCADE)
order_date = models.DateTimeField(auto_now_add=True)
forms.py
from .models import Order, Customer, Product, Price
from django import forms
from django.urls import reverse_lazy
from crispy_forms.helper import FormHelper
from crispy_forms.layout import Submit
from dynamic_forms import DynamicField, DynamicFormMixin
class OrderForm(DynamicFormMixin, forms.ModelForm):
def __init__(self, *args, **kwargs):
super(OrderForm, self).__init__(*args, **kwargs)
self.helper = FormHelper(self)
def price_choices(form):
product = form['product'].value()
return Price.objects.filter(product=product)
def initial_price(form):
product = form['product'].value()
return Price.objects.filter(product=product).first()
product = forms.ModelChoiceField(
queryset=Product.objects.all(),
initial=Product.objects.first(),
label= "Select a Product:",
widget= forms.RadioSelect(
attrs={
'hx-get' : 'prices',
'hx-target' : '#prices',
'hx-swap' : 'innerHTML'
}),
required=True,
)
prices = DynamicField(
forms.ModelChoiceField,
queryset=price_choices,
initial=initial_price,
label= "Select a price:"
)
cust_requests = forms.CharField(
label = 'Enter any specific requests here: (Leave blank if none): ',
required=False,
max_length=500
)
reference_track = forms.FileField(
label = 'Upload a reference track, if applicable.',
required=False,
)
music_file = forms.FileField(
label = 'Upload your project here. Please ensure project has been zipped prior to uploading.',
required=True
)
class Meta:
model= Order
fields = ['product', 'prices', 'cust_requests', 'reference_track', 'music_file']
class CustomerForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
super(CustomerForm, self).__init__(*args, **kwargs)
self.helper = FormHelper(self)
class Meta:
model = Customer
fields = ['first_name', 'last_name', 'phone', 'email']
urls.py
urlpatterns = [
path('', views.StudiosOverview.as_view(), name='musicstudios'),
path('order-details/', views.orderdetails, name='orderdetails'),
path('customer-details/', views.CustomerDetails.as_view(), name='custdetails'),
path('customer-details/upload', views.custupload, name='custupload'),
path('order-details/prices/', views.prices, name='prices'),
path('order-details/upload', views.orderupload, name='orderupload'),
path('cancel/', CancelView.as_view(), name='cancel'),
path('success/', SuccessView.as_view(), name='success'),
path('create-checkout-session/<int:pk>', CreateCheckoutSessionView.as_view(), name='create-checkout-session')
]
views.py
def orderdetails(request):
form = OrderForm()
context = {'form' : form}
template_name = 'musicstudios/order_details.html'
return render(request, template_name, context)
def prices(request):
form = OrderForm(request.GET)
return HttpResponse(form['prices'])
def custupload(request):
if request.POST:
form = forms.CustomerForm(request.POST, request.FILES)
success_url = reverse_lazy('orderdetails')
print(request.FILES)
if form.is_valid():
form.save()
else:
ctx = {'form' : form}
return HttpResponseRedirect(request, 'musicstudios/customer_details.html', ctx)
return HttpResponseRedirect(success_url)
def orderupload(request):
if request.POST:
form = OrderForm()
success_url = reverse_lazy('create-checkout-session')
if form.is_valid():
form.save()
else:
ctx = {'form' : form}
return render(request, 'musicstudios/order_details.html', ctx)
return reverse_lazy(success_url)
class StudiosOverview(View):
def get_context_data(self, **kwargs):
product = Product.objects.all()
prices = Price.objects.all()
context = super(StudiosOverview, self).get_context_data(**kwargs)
context.update({
"product": product,
"prices": prices
})
return context
def get(self, request):
context = {
'page_headline' : 'Studio Services'
}
context.update(sidebar_context)
return render(request, 'musicstudios/overview.html', context)
class CustomerDetails(CreateView):
form_class = forms.CustomerForm
template_name = 'musicstudios/customer_details.html'
stripe.api_key = settings.STRIPE_SECRET_KEY
class CreateCheckoutSessionView(View):
def post(self, request, *args, **kwargs):
product_id = self.kwargs["pk"]
product = Product.objects.get(id=product_id)
domain = "https://lewnytoonsstudios.com"
if settings.DEBUG:
domain = "http://127.0.0.1:8000"
checkout_session = stripe.checkout.Session.create(
line_items=[
{
# Provide the exact Price ID (for example, pr_1234) of the product you want to sell
'price': product.prices.stripe_price_id,
'quantity': 1,
},
],
mode='payment',
success_url=domain + '/success.html',
cancel_url=domain + '/cancel.html',
automatic_tax={'enabled': True},
)
return JsonResponse({
'id' : checkout_session.id
})
class SuccessView(TemplateView):
template_name = "success.html"
class CancelView(TemplateView):
template_name = "cancel.html"
Relevant HTML templates:
customer_details.html
<span class="flex-auto flex-col">
<form method="post" class="py-2" action="{% url 'custupload' %}" enctype="multipart/form-data"; return False;>
{% csrf_token %}
{{ form|crispy }}
<span class="flex justify-end">
<button class="lewny_button my-4" type="submit">Continue to Order</button>
</span>
</form>
</span>
</div>
order_details.html
<span class="flex-auto flex-col">
<form class="py-2" method="post" action="{% url 'orderupload' %}">
{% csrf_token %}
{{ form|crispy }}
<span class="flex justify-end">
<button class="lewny_button my-4" type="submit">Review Your Order</button>
</span>
</form>
</span>
</div>
I have tried several htmx methods of 'getting' the object but have been unable to achieve anything that works. I considered grabbing the most recent object from the database, but this seemed like a very insecure way to go about the solution.
This would seem a job for session variables. Once your customer is created by your save function, you can grab the id and place it in a session variable for later reference.
if form.is_valid():
customer = form.save()
request.session['customer_id'] = customer.id
You can access this wherever you need, either as request.session['customer_id'] (or request.sessions.get('customer_id') to return None if not set) in a functional view or self.request as above in a class based view.
More info in the docs
I have an addpage form where users have to add their card for boots model. Below i will show to you my code.
SO! The problem is my images are not saving at my media directory at all. And so one of the
consequences Card.model doesn't take this images, but rest of the form fields working perfictly. Sorry for my bad english and asking for support!!
Models.py
class Card(models.Model):
category = models.ForeignKey(Category, on_delete=models.CASCADE, verbose_name='Категория')
brand = models.ForeignKey(Brand, on_delete=models.PROTECT, verbose_name='Бренд')
boots_model = models.CharField(max_length=100, db_index=True, verbose_name='Модель бутс')
description = models.TextField(verbose_name='Описание')
slug = AutoSlugField('URL', max_length=70, db_index=True, unique_with=('created', 'boots_model', 'size', 'color', 'price'), populate_from=instance_boots_model, slugify=slugify_value)
price = models.DecimalField(max_digits=10, decimal_places=2, verbose_name='Цена')
# image = models.ImageField(upload_to="photos/%Y/%m/%d/", blank=True, verbose_name='Загрузите фотографии')
created = models.DateTimeField(auto_now_add=True, db_index=True)
updated = models.DateTimeField(auto_now=True)
size = models.DecimalField(max_digits=4, decimal_places=1, verbose_name='Размер')
NEW = 'new'
USED = 'old'
STATEMENT_CHOICES = [
(NEW, 'Новые'),
(USED, 'Б/У')
]
statement = models.CharField(max_length=3, choices=STATEMENT_CHOICES, default=USED, verbose_name='Состояние')
color = models.CharField(max_length=100, db_index=True, verbose_name='цвет')
class Meta:
ordering = ('-created',)
verbose_name = 'Объявление'
verbose_name_plural = 'Объявления'
def __str__(self):
return self.boots_model
def save(self, *args, **kwargs):
self.slug = uuslug(self.slug, instance=self)
super(Card, self).save(*args, **kwargs)
def get_absolute_url(self):
category = self.category
brand = self.brand
return reverse('card', args=[str(category.slug), str(brand.slug), str(self.slug)])
class ImagePhoto(models.Model):
directory = models.ForeignKey(Card, on_delete=models.CASCADE)
image = models.ImageField(upload_to=upload_custom_directory, blank=True, verbose_name='Фотографии')
def __str__(self):
return str(self.image)
class Meta:
verbose_name = 'Фотографии'
verbose_name_plural = 'Фотографии'
forms.py
class AddCardForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.fields['category'].empty_label = "Категория не выбрана"
self.fields['brand'].empty_label = "Бренд не выбран"
# self.fields['boots_model'].help_text = "Старайтесь вводить полное название обуви"
class Meta:
model = Card
fields = ['category', 'brand', 'boots_model', 'description', 'price', 'size', 'statement', 'color']
help_texts = {
'boots_model': 'Название на английском языке с цифрами\nНапример: PREDATOR 18.3 FG',
}
widgets = {
'description': forms.Textarea(attrs={'cols': 30, 'rows': 5, 'placeholder': 'Введите что-нибудь'}),
}
def clean_boots_model(self):
cyrillic_letters = 'абвгдеёжзийклмнопрстуфхцчшщъыьэюяАБВГДЕЁЖЗИЙКЛМНОПРСТУФХЦЧШЩЪЫЬЭЮЯ'
boots_model = self.cleaned_data['boots_model']
for c in boots_model:
if c in cyrillic_letters:
raise ValidationError('Допускается название только на английском языке и цифры')
return boots_model
class AddimgForm(forms.ModelForm):
class Meta:
model = ImagePhoto
fields = ['image']
widgets = {
'image': forms.ClearableFileInput(attrs={'multiple': True}),
}
and finally views.py
def add_page(request):
MyFormSet = inlineformset_factory(Card, ImagePhoto, fields=('image',), extra=0)
if request.method == 'POST':
form = AddCardForm(request.POST)
formset = MyFormSet(request.POST, request.FILES,)
if form.is_valid() and formset.is_valid():
c = form.save(commit=False)
c.save()
for f in formset:
pic = ImagePhoto(directory=c, image=f.cleaned_data('image'))
pic.save()
return redirect('home')
else:
form = AddCardForm()
formset = MyFormSet(queryset=ImagePhoto.objects.none())
return render(request, 'mysite/addpage.html', {'formset': formset, 'form': form, 'title': 'Добавить объявление'})
also addcard.html
<form action="{% url 'addpage' %}" method="post" enctype="multipart/form-data" class="addpage-form">
{% csrf_token %}
<h1>Разместить объявление</h1>
{% for p in form %}
{% if p.help_text %}<p class="help-text">{{ p.help_text|linebreaksbr }}{% endif %}
<p><label class="form-label" for="{{ form.id_for_label }}">{{ p.label }}: </label>{{ p }}</p>
{% endfor %}
{{ formset.management_form }}
<input type="file" name = "files" multiple />{% for f in formset %}
<div class="form-error">{{ f.errors }}</div>
{% endfor %}
<div class="addpage-button-submit">
<button type="submit">Добавить объявление</button>
</div>
</form>
Please forgive me if this has already been asked
I am following a course online, albeit 3 years old, so somethings have been updated since then
However datetime seems to be roughly the same from what I can see.
The problem arises when we use timedelta to filter all the sales in the last month
I have added some test sales over the weeks and when I tried to filter it down to the last 48 hours, nothing
with a bit of testing i went back 12 days (as of this question and a list of items sold back on the 20th February showed up but nothing sooner.
I went into the admin and updated one of them and refreshed the page. and it disappeared completely
Can anyone provide any insight as to what i am missing?
If you need more information please say:
Models.py
from decimal import Decimal
import datetime
from django.conf import settings
from django.db import models
from django.db.models import Count, Sum, Avg
from django.db.models.signals import pre_save, post_save
from django.urls import reverse
from django.utils import timezone
from addresses.models import Address
from billing.models import BillingProfile
from carts.models import Cart
from ecommerce.utils import unique_order_id_generator
from products.models import Product
ORDER_STATUS_CHOICES = (
('created', 'Created'),
('paid', 'Paid'),
('shipped', 'Shipped'),
('refunded', 'Refunded'),
)
class OrderManagerQuerySet(models.query.QuerySet):
def recent(self):
return self.order_by("-updated", "-timestamp")
def by_date(self):
now = timezone.now() - datetime.timedelta(days=12)
return self.filter(updated__day__gte=now.day)
def totals_data(self):
return self.aggregate(Sum("total"), Avg("total"))
def cart_data(self):
return self.aggregate(Sum("cart__products__price"), Avg("cart__products__price"), Count("cart__products"))
def by_status(self, status="shipped"):
return self.filter(status=status)
def not_refunded(self):
return self.exclude(status='refunded')
def by_request(self, request):
billing_profile, created = BillingProfile.objects.new_or_get(request)
return self.filter(billing_profile=billing_profile,)
def not_created(self):
return self.exclude(status='created')
class OrderManager(models.Manager):
def get_queryset(self):
return OrderManagerQuerySet(self.model, using=self._db)
def by_request(self, request):
return self.get_queryset().by_request(request)
def new_or_get(self, billing_profile, cart_obj):
created = False
qs = self.get_queryset().filter(
billing_profile=billing_profile,
cart=cart_obj,
active=True,
status='created'
)
if qs.count() == 1:
obj = qs.first()
else:
obj = self.model.objects.create(
billing_profile=billing_profile,
cart=cart_obj
)
created = True
return obj, created
class Order(models.Model):
billing_profile = models.ForeignKey(BillingProfile, null=True, blank=True, on_delete=models.CASCADE)
order_id = models.CharField(max_length=120, blank=True)
shipping_address = models.ForeignKey(Address, related_name="shipping_address", null=True, blank=True, on_delete=models.CASCADE)
billing_address = models.ForeignKey(Address, related_name="billing_address", null=True, blank=True, on_delete=models.CASCADE)
cart = models.ForeignKey(Cart, on_delete=models.CASCADE)
status = models.CharField(max_length=120, default='created', choices=ORDER_STATUS_CHOICES)
shipping_total = models.DecimalField(default=5.99, max_digits=100, decimal_places=2)
total = models.DecimalField(default=0.00, max_digits=100, decimal_places=2)
active = models.BooleanField(default=True)
updated = models.DateTimeField(auto_now=True)
timestamp = models.DateTimeField(auto_now_add=True)
def __str__(self):
return self.order_id
objects = OrderManager()
class Meta:
ordering = ['-timestamp', '-updated']
def get_absolute_url(self):
return reverse("orders:detail", kwargs={'order_id': self.order_id})
def get_status(self):
if self.status == "refunded":
return "Refunded order"
elif self.status == "shipped":
return "Shipped"
return "Shipping Soon"
def update_total(self):
cart_total = self.cart.total
shipping_total = self.shipping_total
new_total = Decimal(cart_total)+Decimal(shipping_total)
formatted_total = format(new_total, '.2f')
print(type(new_total))
self.total = formatted_total
self.save()
return new_total
def check_done(self):
shipping_address_required = not self.cart.is_digital
shipping_done = False
if shipping_address_required and self.shipping_address:
shipping_done = True
elif shipping_address_required and not self.shipping_address:
shipping_done = False
else:
shipping_done = True
billing_profile = self.billing_profile
billing_address = self.billing_address
total = self.total
if billing_profile and shipping_done and billing_address and total > 0:
return True
return False
def update_purchases(self):
for p in self.cart.products.all():
obj, create = ProductPurchase.objects.get_or_create(
order_id = self.order_id,
product = p,
billing_profile= self.billing_profile,
)
return ProductPurchase.objects.filter(order_id = self.order_id).count()
def mark_paid(self):
if self.status != 'paid':
if self.check_done():
self.status = "paid"
self.save()
self.update_purchases()
return self.status
def pre_save_create_order_id(sender, instance, *args, **kwargs):
if not instance.order_id:
instance.order_id = unique_order_id_generator(instance)
qs= Order.objects.filter(cart=instance.cart).exclude(billing_profile=instance.billing_profile)
if qs.exists():
qs.update(active=False)
pre_save.connect(pre_save_create_order_id, sender=Order)
def post_save_cart_total(sender, instance, created, *args, **kwargs):
if not created:
cart_obj = instance
cart_total = cart_obj.total
cart_id = cart_obj.id
qs = Order.objects.filter(cart__id=cart_id)
if qs.count() ==1:
order_obj = qs.first()
order_obj.update_total()
post_save.connect(post_save_cart_total,sender=Cart)
def post_save_order(sender, instance, created, *args, **kwargs):
print("running")
if created:
print("updating...first")
instance.update_total()
post_save.connect(post_save_order,sender=Order)
class ProductPurchaseQuerySet(models.query.QuerySet):
def active(self):
return self.filter(refunded=False)
def digital(self):
return self.filter(product__is_digital=True)
def by_request(self, request):
billing_profile, created = BillingProfile.objects.new_or_get(request)
return self.filter(billing_profile=billing_profile,)
class ProductPurchaseManager(models.Manager):
def get_queryset(self):
return ProductPurchaseQuerySet(self.model, using=self._db)
def all(self):
return self.get_queryset().active()
def digital(self):
return self.get_queryset().active().digital()
def by_request(self, request):
return self.get_queryset().by_request(request)
def products_by_id(self, request):
qs = self.by_request(request).digital()
ids_ = [x.product.id for x in qs]
return ids_
def products_by_request(self, request):
ids_ = self.products_by_id(request)
product_qs = Product.objects.filter(id__in=ids_).distinct()
return product_qs
class ProductPurchase(models.Model):
order_id = models.CharField(max_length=120)
billing_profile = models.ForeignKey(BillingProfile, on_delete=models.CASCADE) # billingprofile.productpurchase_set.all()
product = models.ForeignKey(Product, on_delete=models.CASCADE) # product.productpurchase_set.count()
refunded = models.BooleanField(default=False)
updated = models.DateTimeField(auto_now=True)
timestamp = models.DateTimeField(auto_now_add=True)
objects = ProductPurchaseManager()
def __str__(self):
return self.product.title
Views.py
from django.contrib.auth.mixins import LoginRequiredMixin
from django.db.models import Count, Sum, Avg
from django.http import HttpResponse
from django.views.generic import TemplateView
from django.shortcuts import render
from orders.models import Order
class SalesView(LoginRequiredMixin, TemplateView):
template_name = 'analytics/sales.html'
def dispatch(self, *args, **kwargs):
user = self.request.user
if not user.is_staff:
return render(self.request, "401.html", {})
return super(SalesView, self).dispatch(*args, **kwargs)
def get_context_data(self, *args, **kwargs):
context = super(SalesView, self).get_context_data(*args, **kwargs)
qs = Order.objects.all().by_date()
context['orders'] = qs
context['recent_orders'] = qs.recent().not_refunded()
context['recent_orders_data'] = context['recent_orders'].totals_data()
context['recent_orders_cart_data'] = context['recent_orders'].cart_data()
context['shipped_orders'] = qs.recent().not_refunded().by_status("shipped")
context['shipped_orders_data'] = context['shipped_orders'].totals_data()
context['paid_orders'] = qs.recent().not_refunded().by_status("paid")
context['paid_orders_total'] = context['paid_orders'].totals_data()
return context
Sales.html
{% extends "base.html" %}
{% block content%}
<div class="row">
<div class="col-12">
<h1>Sales Data</h1>
</div>
</div>
<div class="row">
<div class="col">
<p>Recent Toal: £{% if recent_orders_data.total__sum %} {{ recent_orders_data.total__sum }}{% else %}0 {% endif %}</p>
<ol>
{% for order in recent_orders %}
<li>{{ order.order_id}}
{{ order.total}}
{{ order.updated}} </li>
{% endfor %}
</ol>
</div>
<div class="col">
<p>Shipped Total: £{% if shipped_orders_data.total__sum %}{{ shipped_orders_data.total__sum }}{% else %}0{% endif %}</p>
<ol>
{% for order in shipped_orders %}
<li>{{ order.order_id}}
{{ order.total}}
{{ order.updated}}</li>
{% endfor %}
</ol>
</div>
<div class="col">
<p>Paid Totals: £{% if shipped_orders_data.total__sum %}{{ paid_orders_total.total__sum }}{% else %}0{% endif %}</p>
<ol>
{% for order in paid_orders %}
<li>{{ order.order_id}}
{{ order.total}}
{{ order.updated}}</li>
{% endfor %}
</ol>
</div>
</div>
{% endblock %}
In your filter by_date method you are using a filter on the day not the date. This won't help as it'll return the day of the month. So if the date was 2020-03-11 it would return 29 for now.day. If you change .day for .date() as well as updated__day__gte for updated__date__gte it should be okay
def by_date(self):
now = timezone.now() - datetime.timedelta(days=12)
return self.filter(updated__date__gte=now.date())
The following should work fine:
now = timezone.now().date() - datetime.timedelta(days=12)
return self.filter(updated__date__gte=now)
I would like to get the total amount of followers attached to the models using in models :
class Project(models.Model):
owner = models.ForeignKey(User, related_name='project_created_by', on_delete=models.CASCADE)
name = models.CharField(max_length=100)
description = models.TextField(max_length=150, blank=True, null=True)
followers = models.ManyToManyField(settings.AUTH_USER_MODEL, related_name='followers', blank=True)
created = models.DateTimeField(auto_now_add=True)
last_modefied = models.DateTimeField(auto_now=True)
def __str__(self):
return self.name
Here is the class
class ProjectListView(ListView):
template_name = 'projectmanagement/project.html'
context_object_name = 'projects'
def get_queryset(self):
queryset = Project.objects.filter(owner=self.request.user).order_by("name")
return queryset
def get_context_data(self, *args, **kwargs):
context = super(ProjectListView, self).get_context_data(*args, **kwargs)
project = Project.objects.get(pk=12) <-- HERE -->
context['followers'] = project.followers.filter(followers=project).count()
return context
You can .annotate(..) [Django-doc] the queryset of your Product with the number of followers:
from django.db.models import Count
class ProjectListView(ListView):
model = Project
template_name = 'projectmanagement/project.html'
context_object_name = 'projects'
def get_queryset(self):
return super().get_queryset().annotate(
nfollowers=Count('followers')
).filter(
owner=self.request.user
).order_by('name')
Now all projects in the context data will have an extra attribute nfollowers with the number of followers.
You can thus render this for example with:
{% for project in projects %}
{{ project.name }}, followers: {{ project.nfollowers }}<br>
{% endfor %}
Alright, I'm fairly new to this, I've been working on my Project for a couple months now and I'd like to create URLs that accept multiple parameters to call a View. A sample URL would look like this:
http://www.sample.com/builders//m//
I've got this implemented successfully, by overriding get_object in my DetailView, but I'm wondering if there is a better/easier method for accomplishing this or if this is considered a bad practice. Any guidance would be appreciated.
urls.py
urlpatterns = [
# url(r'^$', builder_list, name='list'),
# url(r'^create/$', builder_create, name='create'),
# url(r'^(?P<slug>[\w-]+)/$', builder_detail, name='detail'),
# url(r'^(?P<slug>[\w-]+)/edit/$', builder_update, name='update'),
# url(r'^(?P<slug>[\w-]+)/delete/$', builder_delete, name='delete'),
# url(r'^$', builder_list, name='sub_list'),
# url(r'^m/create/$', sub_create, name='sub_create'),
url(r'^(?P<builder>[\w-]+)/m/(?P<market>[\w-]+)/$', sub_detail, name='sub_detail'),
# url(r'^m/(?P<slug>[\w-]+)/edit/$', sub_update, name='sub_update'),
# url(r'^m/(?P<slug>[\w-]+)/delete/$', sub_delete, name='sub_delete'),
]
views.py
class BuilderSubDetailView(DetailView):
model = BuilderSub
template_name = "builders/sub_detail.html"
def get_context_data(self, **kwargs):
context = super(BuilderSubDetailView, self).get_context_data(**kwargs)
context['now'] = timezone.now()
print(context)
return context
def get_object(self, queryset=None):
if queryset is None:
queryset = self.get_queryset()
# Next, try looking up by primary key.
builder = self.kwargs['builder']
builder_id = Builder.objects.filter(slug=builder).first().pk
market = self.kwargs['market']
market_id = Market.objects.filter(slug=market).first().pk
if builder is not None and market is not None:
queryset = BuilderSub.objects.filter(parent=builder_id).filter(market=market_id)
# If none of those are defined, it's an error.
if builder is None or market is None:
raise AttributeError("Generic detail view %s must be called with "
"Builder and Market"
% self.__class__.__name__)
try:
# Get the single item from the filtered queryset
obj = queryset.get()
except queryset.model.DoesNotExist:
raise Http404("No %(verbose_name)s found matching the query") % \
{'verbose_name': queryset.model._meta.verbose_name}
return obj
And models.py for reference -- also is there any problem with my get_absolute_url function?
class Builder(models.Model):
added_by = models.ForeignKey(settings.AUTH_USER_MODEL, default=1)
company_name = models.CharField(max_length=80, help_text="Full Company Name", unique=True)
short_name = models.CharField(help_text="Short Company Name", max_length=30)
slug = models.SlugField(unique=True)
website = models.CharField(max_length=80, help_text="Format: www.[website].com")
logo = models.ImageField(blank=True, null=True)
timestamp = models.DateTimeField(auto_now_add=True)
info = RedactorField(verbose_name=u'Company Info')
def show_website_url(self):
return format_html("<a href='{url}'>{url}</a>", url=self.website)
def __str__(self):
return self.short_name
class BuilderSub(models.Model):
parent = models.ForeignKey(Builder)
market = models.ForeignKey(Market, null=True, blank=True)
details = RedactorField(verbose_name=u'Details', blank=True, null=True)
main_contact = models.ForeignKey(Person, blank=True, null=True)
def __str__(self):
return "{}: {} - {}".format(self.pk, self.market.name, self.parent.short_name)
def get_absolute_url(self):
return reverse('builders:sub_detail', kwargs={'market': self.market.slug, 'builder': self.parent.slug})
def pre_save_builder_reciever(sender, instance, *args, **kwargs):
instance.slug = slugify(instance.short_name)
pre_save.connect(pre_save_builder_reciever, sender=Builder)
I'm not 100% sure I'm my BuilderSub Model is the appropriate way to handle the Relationship between the overall Builder (company) and the Markets they serve so any guidance there would be appreciated as well.
Yes there is indeed a more ethical way to do this. DetailView is meant to deal with only one object. ListView however gets the job done!
I have replaced builder and market with city and category.
I am also a beginner. Hope I have answered your question :)
views.py
class EntryListView(generic.ListView):
template_name = 'myapp/category.html'
context_object_name = 'entry'
def get_queryset(self):
city_id = self.kwargs['city']
category_id = self.kwargs['category']
entry = Entry.objects.all().filter(city=city_id).filter(category=category_id)
return entry
urls.py
url(r'^(?P<city>[0-9]+)/(?P<category>[0-9]+)/$', views.EntryListView.as_view(), name='entry'),
category.html
{% extends 'myapp/base.html' %}
{% block body %}
<table>
{% for new in entry %}
<tr>
<td>
<img src = "{{new.image_url}}">
<br>
<b>Name :</b> {{new.name}}<br>
{% if new.phone %}
<B>Phone No. :</B> {{new.phone}}<br>
{% endif %}
<b>Address :</b> {{new.address}}<br>
</td>
</tr>
{% endfor %}
{% endblock %}
models.py
class Entry(models.Model):
city = models.ForeignKey(City, on_delete=models.CASCADE)
category = models.ForeignKey(Category, on_delete=models.CASCADE)
name = models.CharField(max_length=250)
phone = models.IntegerField(null=True)
address = models.CharField(max_length=250)
image_url = models.CharField(max_length=500)
def __str__(self):
return self.name