Django extending the User model in ECommerce application - python

I have a django ecommerce project that works fine till I decided to improve it. User place order and every time they place an order they have to always input their details (name, emil, address etc) , I decided to upgrade the application so if user had registered before no need to enter details again .
orders/models.py
from django.db import models
from django.contrib.auth.models import User
from shop.models import Product
from decimal import Decimal
from django.core.validators import MinValueValidator,MaxValueValidator
from coupons.models import Coupons
class order(models.Model):
first_name = models.CharField(max_length=50)
last_name = models.CharField(max_length=50)
email = models.EmailField()
address = models.CharField(max_length=250)
postal_code = models.CharField(max_length=50)
city = models.CharField(max_length=100)
created = models.DateTimeField(auto_now_add=True)
updated = models.DateTimeField(auto_now=True)
paid = models.BooleanField(default=False)
coupon = models.ForeignKey(Coupons,related_name='orders',on_delete=models.CASCADE ,null=True,blank=True)
discount = models.IntegerField(default=0,validators=[MinValueValidator(0),MaxValueValidator(100)])
class Meta:
ordering = ('-created',)
verbose_name = "order"
verbose_name_plural = "orders"
def __str__(self):
return 'Order {}'.format(self.id)
def get_total_cost(self):
return sum(item.get_cost() for item in self.items.all())
def get_total_cost_after_discount(self):
total_cost = sum(item.get_cost() for item in self.items.all())
return total_cost - total_cost * (self.discount / Decimal('100'))
class OrderItem(models.Model):
order = models.ForeignKey(order,related_name='items',on_delete=models.CASCADE)
product = models.ForeignKey(Product,related_name='order_items',on_delete=models.CASCADE)
price = models.DecimalField(max_digits=10,decimal_places=2)
quantity = models.PositiveIntegerField(default=1)
class Meta:
verbose_name = "OrderItem"
verbose_name_plural = "OrderItems"
def __str__(self):
return '{}'.format(self.id)
def get_cost(self):
return self.price * self.quantity
I create account app and extended django user so that user can register address so that the user does not have to type it every time when place an order.
accounts/models.py
class UserProfile(models.Model):
user = models.OneToOneField(User,related_name='UserProfiles',on_delete=models.CASCADE)
country = models.CharField(max_length=300, default='Saudi Arabia')
city = models.CharField(max_length=100, default='')
phone = models.CharField(max_length=15,default='')
image = models.ImageField(upload_to='profile_image', blank=True)
created_date = models.DateTimeField(auto_now_add=True)
updated_date = models.DateTimeField(null=True)
def __str__(self):
return self.user.username
def create_profile(sender, **kwargs):
if kwargs['created']:
user_profile = UserProfile.objects.create(user=kwargs['instance'])
post_save.connect(create_profile, sender=User)
and rewrite:
orders/models.py
class order(models.Model):
user = models.ForeignKey(User,on_delete=models.DO_NOTHING)
address = models.ForeignKey(UserProfile,on_delete=models.DO_NOTHING)
created = models.DateTimeField(auto_now_add=True)
updated = models.DateTimeField(auto_now=True)
paid = models.BooleanField(default=False)
coupon = models.ForeignKey(Coupons,related_name='orders',on_delete=models.CASCADE ,null=True,blank=True)
discount = models.IntegerField(default=0,validators=[MinValueValidator(0),MaxValueValidator(100)])
now my problem is how can I fix the view to show form of user address with information if user entered before or the user fill form with address
views.py
#login_required
def order_create(request):
cart = Cart(request)
if request.method == 'POST':
form = OrderCreateForm(request.POST)
if form.is_valid():
order = form.save(commit=False)
if cart.coupon:
order.coupon = cart.coupon
order.discount = cart.coupon.discount
order.save()
for item in cart:
OrderItem.objects.create(
# user = User.username,
order=order,
product=item['product'],
price=item['price'],
quantity=item['quantity'])
cart.clear()
context = {
'order':order,
}
return render(request, 'created.html',context)
else:
form = OrderCreateForm()
context = {
'cart':cart,
'form':form
}
return render(request, 'create.html',context)
orders/forms.py
from django import forms
from .models import order
class OrderCreateForm(forms.ModelForm):
class Meta:
model = order
fields = ('first_name','last_name','email','address','postal_code','city',)

I don't see your OrderCreateForm, but I'm assuming it has fields that match the ones in UserProfile. If so, try changing the view code to pull data from the profile to populate the initial data:
#login_required
def order_create(request):
cart = Cart(request)
if request.method == 'POST':
form = OrderCreateForm(request.POST)
if form.is_valid():
order = form.save(commit=False)
if cart.coupon:
order.coupon = cart.coupon
order.discount = cart.coupon.discount
order.save()
for item in cart:
OrderItem.objects.create(
# user = User.username,
order=order,
product=item['product'],
price=item['price'],
quantity=item['quantity'])
cart.clear()
context = {
'order':order,
}
return render(request, 'created.html',context)
else:
if request.user and hasattr(request.user, 'UserProfiles'):
profile = request.user.UserProfiles
initial = {
'first_name', request.user.first_name,
'last_name', request.user.last_name,
'email', request.user.email,
'address': profile.address,
'city': profile.city
}
else:
initial = {}
form = OrderCreateForm(initial=initial)
context = {
'cart':cart,
'form':form
}
return render(request, 'create.html',context)

Related

Django save form with foreign key

I am currently trying to create a form where users get to fill in their details after creating an account. The idea is that every user, after registration, gets redirected to this form page to fill it out. To achieve this, i'm using foreign keys.However it doesn't save to database.
models.py
class User(AbstractUser):
pass
def __str__(self):
return self.username
class Detail(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE, null=False, default="")
first_name = models.CharField(max_length=200, default="")
last_name = models.CharField(max_length=255, default="")
class Meta:
verbose_name_plural = "Detail"
def __str__(self):
return self.first_name+ " "+self.last_name
forms.py
class Details(forms.ModelForm):
class Meta:
model = Detail
fields = "__all__"
widgets={
"user": forms.TextInput()
}
views.py
def details(request):
if request.method =="POST":
form = Details(request.POST)
if form.is_valid():
detail = form.save(commit=False)
detail.user = request.user
detail.first_name = detail.first_name.lower()
detail.last_name = detail.last_name.lower()
detail.save()
return redirect("admin:index")
else:
form = Details(initial={"user":request.user.username})
return render(request, "details.html", {"form":form})
You need to exclue user field from ModelForm like this
form.py
class Details(forms.ModelForm):
class Meta:
model = Detail
fields = "__all__"
exclude =["user"]
views.py
def details(request):
if request.method =="POST":
form = Details(request.POST)
if form.is_valid():
detail = form.save(commit=False)
detail.user = request.user
detail.first_name = detail.first_name.lower()
detail.last_name = detail.last_name.lower()
detail.save()
return redirect("admin:index")
else:
form = Details()
return render(request, "details.html", {"form":form})

How do I fail the booking if the booked boolean field is set to True in django?

I am having trouble making if/else work in my django app, I want to check if a listing is booked, booked is a boolean field and the listing is a foreign key to the Booking class where the user selects a listing to book. Now I just want to know how that foreign key can be checked if the listing is booked so it fails to book and gives an error.
views.py
#login_required
def profile(request):
if request.method == "GET":
tickets = models.Booking.objects.all().filter(user=request.user)
return render(request, "profile.html", {'form': forms.BookingForm(), 'tickets': tickets})
else:
try:
form = forms.BookingForm(request.POST)
new_ticket = form.save(commit=False)
new_ticket.user = request.user
new_ticket.save()
messages.success(request, 'Booking Created Successfully')
return redirect('profile')
except ValueError:
return render(request, 'profile.html', {'form': forms.BookingForm()})
models.py
class Listing(models.Model):
title = models.CharField(max_length=50)
content = models.TextField(max_length=755)
price = MoneyField(max_digits=5, decimal_places=2)
booked = models.BooleanField(default=False)
seller = models.ForeignKey(User, on_delete=models.CASCADE)
# avail_days = models.ForeignKey(Days, on_delete=models.CASCADE)
def __str__(self):
return self.title
class Booking(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE)
listing = models.ForeignKey(Listing, on_delete=models.CASCADE) # how to check if listing is booked and deny if it is True ?
# day = models.OneToOneField(Days, on_delete=models.CASCADE)
date_booked = models.DateField(auto_now_add=True)
def __str__(self):
return self.user.username
If you want to use booked field in Listing model, you can do this (you can also render Listing objects with booked=False):
views.py
#login_required
def profile(request):
if request.method == "GET":
tickets = models.Booking.objects.all().filter(user=request.user)
return render(request, "profile.html", {'form': forms.BookingForm(), 'tickets': tickets})
else:
try:
form = forms.BookingForm(request.POST)
new_ticket = form.save(commit=False)
# here you can do something like
if new_ticket.listing.booked:
messages.error(request, 'Booked Already')
return redirect('profile')
new_ticket.user = request.user
new_ticket.save()
messages.success(request, 'Booking Created Successfully')
return redirect('profile')
except ValueError:
return render(request, 'profile.html', {'form': forms.BookingForm()})
models.py
class Listing(models.Model):
title = models.CharField(max_length=50)
content = models.TextField(max_length=755)
price = MoneyField(max_digits=5, decimal_places=2)
booked = models.BooleanField(default=False)
seller = models.ForeignKey(User, on_delete=models.CASCADE)
# avail_days = models.ForeignKey(Days, on_delete=models.CASCADE)
def __str__(self):
return self.title
class Booking(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE)
listing = models.ForeignKey(Listing, on_delete=models.CASCADE) # how to check if listing is booked and deny if it is True ?
# day = models.OneToOneField(Days, on_delete=models.CASCADE)
date_booked = models.DateField(auto_now_add=True)
def __str__(self):
return self.user.username
def save(self, *args, **kwargs):
self.listing.booked = True
self.listing.save()
super().save(*args, **kwargs)

MultipleObjectsReturned at /checkout/ get() returned more than one Order -- it returned 2

Hi I'm trying to develop an e-commerce website using Django. I have two models and views on separate folders, one is an order model and view, another is the checkout model and view. The order view creates a new order everytime an item is added to the cart. And the checkout view creates a new billing address. I want to associate the billing address that is created to the order when the checkout form is submitted. But for some reason, it's not happening. It throws an error:
MultipleObjectsReturned at /checkout/
get() returned more than one Order -- it returned 2!
What is the problem?
My orders.models.py:
from django.db import models
from shopping_cart.models import Cart
from django.contrib.auth import get_user_model
from accounts2.models import BillingAddress
STATUS_CHOICES = (
("Started", "Started"),
("Abandoned", "Abandoned"),
("Finished", "Finished"),
)
User = get_user_model()
class Order(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE, blank=True, null=True)
order_id = models.CharField(max_length=120, default='ABC', unique=True)
cart = models.ForeignKey(Cart, on_delete=models.CASCADE)
status = models.CharField(max_length=120, choices=STATUS_CHOICES, default="Started")
sub_total = models.DecimalField(default=10.99, max_digits=1000, decimal_places=2)
tax_total = models.DecimalField(default=0.00, max_digits=1000, decimal_places=2)
final_total = models.DecimalField(default=10.99, max_digits=1000, decimal_places=2)
timestamp = models.DateTimeField(auto_now_add=True, auto_now=False)
billing_address = models.ForeignKey(BillingAddress, on_delete=models.SET_NULL, blank= True, null=True)
My orders.views.py:
#login_required
def order(request):
try:
the_id = request.session['cart_id']
cart = Cart.objects.get(id=the_id)
except:
the_id = None
return redirect(reverse("myshop-home"))
try:
new_order = Order.objects.get(cart=cart)
except Order.DoesNotExist:
new_order = Order(cart=cart)
new_order.cart = cart
new_order.user = request.user
new_order.order_id = id_generator()
new_order.save()
except:
return redirect(reverse("cart"))
if new_order.status == "Finished":
#cart.delete()
del request.session['cart_id']
del request.session['items_total']
return redirect(reverse("cart"))
context = {"address_form": address_form, "cart": cart}
template = "orders/checkout.html"
return render(request, template, context)
My accounts.models.py:
class BillingAddress(models.Model):
user = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete= models.CASCADE)
street_address = models.CharField(max_length=100)
apartment_address = models.CharField(max_length=100)
country = CountryField(multiple=False)
zip = models.CharField(max_length=100)
class Meta:
verbose_name_plural = "Billing Addresses"
My accounts.views.py:
class CheckoutView(LoginRequiredMixin, View):
def get(self, *args, **kwargs):
the_id = self.request.session['cart_id']
cart = Cart.objects.get(id=the_id)
form = CheckoutForm()
context = {"form": form, "cart": cart}
return render(self.request, "orders/checkout.html", context)
def post(self, *args, **kwargs):
form = CheckoutForm(self.request.POST or None)
try:
order = Order.objects.get(user = self.request.user)
if form.is_valid():
street_address = form.cleaned_data.get('street_address')
apartment_address = form.cleaned_data.get('apartment_address')
country = form.cleaned_data.get('country')
zip = form.cleaned_data.get('zip')
# same_shipping_address = form.cleaned_data.get('same_billing_address')
# save_info = form.cleaned_data.get('save_info')
payment_option = form.cleaned_data.get('payment_option')
billing_address = BillingAddress(
user = self.request.user,
street_address = street_address,
apartment_address = apartment_address,
country = country,
zip = zip
)
billing_address.save()
order.billing_address = billing_address
order.save()
return redirect('checkout')
messages.warning(self.request, "Failed checkout")
return redirect('checkout')
except ObjectDoesNotExist:
messages.warning(self.request, "You do not have an active order")
return redirect('/')
Use Order.objects.filter(user=user).last() to get the newest row or try to remember order's id when creating an order and filter with Order.objects.get(id=id)

django modelform more then one foreign key using exclude

here user i want user and userprofile. useing exclude. when i add userprofile error 'WSGIRequest' object has no attribute 'userprofile'
modelform
class ProductForm(forms.ModelForm):
class Meta:
model = Product
fields = ('categories', 'title', 'description', 'image', 'price')
exclude = ('user','userprofile')
view
#login_required
def productpost(request):
form = ProductForm()
if request.method == "POST":
form = ProductForm(request.POST, request.FILES)
if form.is_valid():
form = form.save(commit=False)
form.user = request.user
form.userprofile = request.userprofile
form.save()
return success(request)
else:
print("The Form Is Invalid")
return render(request, 'product/postproduct.html', {'form': form})
Model
class Product(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE)
userprofileinfo = models.ForeignKey(UserProfileInfo, on_delete=models.CASCADE)
categories = models.ForeignKey(Categories, on_delete=models.CASCADE)
title = models.CharField(max_length=255)
description = models.TextField()
image = models.FileField()
price = models.IntegerField()
pub_date = models.DateTimeField('date published', auto_now_add=True)
def __str__(self):
return self.title
def was_published_recently(self):
return self.pub_date >= timezone.now() - datetime.timedelta(days=1)

Django: How to filter ForeignKey choices by request.user with ModelFormSet

I just want to filter select field in a form, regarding a currently logged user. Every user has own categories and budgets - I want to display only a models related with a currently logged user. I've tried stuff with filtering before is_valid field, but with no result. Please, find my code below:
views.py
#login_required
def day_data_multiadd(request, no_of_lines=0):
no_of_lines = int(no_of_lines)
CostFormSet = modelformset_factory(Cost, form=DataAddForm, extra=no_of_lines)
if request.method == 'POST' and 'form' in request.POST:
formset = CostFormSet(request.POST, request.FILES)
if formset.is_valid():
for form in formset.forms:
f = form.save(commit=False)
f.user = request.user
f.save()
else:
formset = CostFormSet(queryset=Cost.objects.none())
if request.method == 'POST' and 'no_line' in request.POST:
generate_form = MultiaddGenerateForm(request.POST)
if generate_form.is_valid():
cd = generate_form.cleaned_data
return HttpResponseRedirect(reverse('core_sm:day_data_multiadd', args=(cd['formy'], )))
else:
generate_form = MultiaddGenerateForm()
return render(request, 'core_sm/costs/multi_add.html', {'formset': formset,
'no_of_lines': no_of_lines,
'generate_form': generate_form})
forms.py
class DataAddForm(forms.ModelForm):
class Meta:
model = Cost
fields = ['title', 'value', 'publish', 'category', 'budget']
exclude = ['user']
labels = {
'title': _('Tytuł'),
'value': _('Wartość'),
'publish': _('Data'),
'category': _('Kategoria'),
'budget': _('Budżet')
}
models.py
class Category(models.Model):
user = models.ForeignKey(settings.AUTH_USER_MODEL)
title = models.CharField(max_length=200)
publish = models.DateField(auto_now_add=True)
updated = models.DateField(auto_now=True)
class Meta:
ordering = ('-publish',)
def __str__(self):
return self.title
def get_absolute_ur(self):
return reverse('core_sm:category_detail', args=[self.id])
class Budget(models.Model):
user = models.ForeignKey(settings.AUTH_USER_MODEL)
title = models.CharField(max_length=200, db_index=True)
value = models.DecimalField(decimal_places=2, max_digits=10)
publish = models.DateField(auto_now_add=True)
updated = models.DateField(auto_now=True)
class Meta:
ordering = ('-publish',)
def __str__(self):
return self.title
def get_absolute_url(self):
return reverse('core_sm:budget_detail', args=[self.id])
class Cost(models.Model):
user = models.ForeignKey(settings.AUTH_USER_MODEL)
budget = models.ForeignKey(Budget, related_name='cost')
category = models.ForeignKey(Category, related_name='cost')
title = models.CharField(max_length=200, db_index=True)
publish = models.DateField(default=timezone.now)
created = models.DateField(auto_now_add=True)
updated = models.DateField(auto_now=True)
value = models.DecimalField(max_digits=10, decimal_places=2)
class Meta:
ordering = ('-publish',)
def get_absolute_url(self):
return reverse('core_sm:stats_detail', args=[self.publish.year,
self.publish.strftime('%y'),
self.publish.strftime('%m'),
self.publish.strftime('%d')])
def __str__(self):
return self.title
According to the docs you can pass custom parameters to formsets forms.
class DataAddForm(forms.ModelForm):
class Meta:
model = Cost
fields = ['title', 'value', 'publish', 'category', 'budget']
exclude = ['user']
labels = {
'title': _('Tytuł'),
'value': _('Wartość'),
'publish': _('Data'),
'category': _('Kategoria'),
'budget': _('Budżet')
}
def __init__(self, *args, **kwargs):
user = kwargs.pop('user')
self.fields['category'].queryset = Category.objects.filter(user=user)
self.fields['budget'].queryset = Budget.objects.filter(user=user)
super(DataAddForm, self).__init__(*args, **kwargs)
views.py
formset = CostFormSet(queryset=Cost.objects.none(), form_kwargs={'user': request.user})

Categories

Resources