I'm new in Python and hope that someone can help me. I realise that this is probably not a unique question, but please be sympathetic.
I'm working on web-application (it's a bookstore). I make a cart and the proccess of forming an order. Now I'm trying to make a usr profile but unfortunately, I don't know how to display all user orders and make it possible to cahnge orders (change quantity of books for exmpl.) and how to make changable user profile information.
I realised the following logic:
User creates the cart and then create the order. After order creation cart also is in database.
To summarise the above, main questions are:
How to add all information from user cart (that was formed into the order) to user's profile?
How to make user's data and order's/cart's data possible to change in the user's profile?
How to display several user's orders in separate rows in HTML table (cause in my template all orders are in one row)?
Cart models:
User = get_user_model()
class Cart(models.Model):
customer = models.ForeignKey(
User, null=True, blank=True,
related_name="Customer",
verbose_name="Customer",
on_delete=models.PROTECT
)
#property
def total_price_cart(self):
goods = self.goods.all()
total_price_cart = 0
for good in goods:
total_price_cart += good.total_price
return total_price_cart
def __str__(self):
return str(self.pk)
class BooksInCart(models.Model):
cart = models.ForeignKey(
Cart,
related_name="goods",
on_delete=models.CASCADE,
verbose_name="Cart"
)
book = models.ForeignKey(
Book,
on_delete=models.PROTECT,
verbose_name='Book',
)
quantity = models.IntegerField(
verbose_name="Quantity",
default=1
)
price = models.DecimalField(
verbose_name='Price',
max_digits=5,
decimal_places=2,
)
#property
def total_price(self):
return self.price * self.quantity
Cart views:
class CartUpdate(View):
def post(self, request):
action = request.POST.get('submit')
if action == "save_cart":
cart_id = self.request.session.get('cart_id')
cart, created = models.Cart.objects.get_or_create(
pk=cart_id,
defaults={},
)
if created:
self.request.session['cart_id'] = cart.pk
goods = cart.goods.all()
if goods:
for key, value in request.POST.items():
if "quantityforgood_" in key:
pk = int(key.split('_')[1])
good = goods.get(pk=pk)
good.quantity = int(value)
good.save()
return HttpResponseRedirect(reverse_lazy("carts:cart_edit"))
elif action == "create_order":
return HttpResponseRedirect(reverse_lazy('order:create_order'))
else:
return HttpResponseRedirect(reverse_lazy("carts:cart_edit"))
class CartView(generic.DetailView):
template_name = 'carts/cart_edit.html'
model = models.Cart
def get_object(self, queryset=None):
cart_id = self.request.session.get('cart_id')
cart, created = models.Cart.objects.get_or_create(
pk=cart_id,
defaults={},
)
if created:
self.request.session['cart_id'] = cart.pk
book_id = self.request.GET.get('book_pk')
if book_id:
book = Book.objects.get(pk=int(book_id))
book_in_cart, flat_created = models.BooksInCart.objects.update_or_create(
cart=cart,
book=book,
defaults={
'price': book.price
}
)
if not flat_created:
q = book_in_cart.quantity + 1
book_in_cart.quantity = q
book_in_cart.price = book_in_cart.book.price * q
else:
book_in_cart.price = book.price
book_in_cart.save()
return cart
class DeleteGoodInCartView(generic.DeleteView):
model = models.BooksInCart
template_name = 'carts/delete_book_in_cart.html'
success_url = reverse_lazy("carts:cart_edit")
Order models:
User = get_user_model()
class CustomSession(Session):
cart = models.ForeignKey(
Cart,
on_delete=models.CASCADE
)
class Meta:
app_label = 'cart_id'
class Status(models.Model):
name = models.CharField(max_length=30)
def __str__(self):
return self.name
class Meta:
verbose_name = "Status"
verbose_name_plural = "Statuses"
class Order(models.Model):
user = models.ForeignKey(
User,
on_delete=models.PROTECT,
related_name='orders'
)
cart = models.OneToOneField(
Cart,
on_delete=models.PROTECT,
verbose_name="Cart"
)
status = models.ForeignKey(
Status,
on_delete=models.PROTECT
)
contact_info = models.TextField(
verbose_name="Contact info",
)
created = models.DateTimeField(
verbose_name="Created",
auto_now=False,
auto_now_add=True
)
updated = models.DateTimeField(
verbose_name="Updated",
auto_now=True,
auto_now_add=False
)
def __str__(self):
return self.contact_info
class Meta:
verbose_name = "Order"
verbose_name_plural = "Orders"
Cart views:
class CreateOrderView(generic.FormView):
form_class = forms.OrderCreateForm
template_name = 'order/create_order.html'
success_url = reverse_lazy("order:success")
def form_valid(self, form):
cart_id = self.request.session.get('cart_id')
cart, created = carts_models.Cart.objects.get_or_create(
pk=cart_id,
defaults={},
)
if created:
return HttpResponseRedirect(reverse_lazy('carts:cart_edit'))
info = form.cleaned_data.get('contact_info')
status = models.Status.objects.get(pk=1)
user = self.request.user
order = models.Order.objects.update_or_create(
cart=cart,
contact_info=info,
status=status,
user=user,
)
self.request.session.delete('cart_id')
if self.request.user.is_authenticated:
cart_id = self.request.session.get('cart_id')
customer1 = carts_models.Cart.objects.get(pk=cart_id)
customer1.customer = self.request.user
customer1.save()
return super().form_valid(form)
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
cart_id = self.request.session.get('cart_id')
cart, created = carts_models.Cart.objects.get_or_create(
pk=cart_id,
defaults={},
)
context['object'] = cart
return context
def get_success_url(self) -> str:
del self.request.session['cart_id']
return super().get_success_url()
def success(requsest):
return render(requsest, 'order/success.html')
User profile views:
class ProfileView(generic.DetailView):
def get(self, request, *args, **kwargs):
user = get_object_or_404(AppUser, pk=kwargs['pk'])
return render(request, 'app_profiles/profile_view.html', context={
'user': user,
'order': Order.objects.filter(user=request.user),
})
User profile template:
<thead>
<tr>
<th scope="col">Goods</th>
<th scope="col">Quantity</th>
<th scope="col">Total price</th>
<th scope="col">Status</th>
<th scope="col">Created</th>
<th scope="col">Updated</th>
</tr>
</thead>
<tbody>
<tr>
{% for order in order.all %}
<td>{{ order.created }}</td>
<td>{{ order.updated }}</td>
<td>{{ order.status }}</td>
{% endfor %}
I'll try to answer your questions best i can based on what i think you're asking.
Your questions:
How to add all information from user cart (that was formed into the
order) to user's profile?
How to make user's data and order's/cart's data possible to change
in the user's profile?
How to display several user's orders in separate rows in HTML table
(cause in my template all orders are in one row)?
1: Make a foreign key relation from the cart to the user using Django's ORM.
Example:
# Create your models here.
class Someclass(models.Model):
user = models.CharField(max_length=255, default='none')
subject = models.CharField(max_length=255, default='none')
date = models.DateField(auto_now_add=True)
def __str__(self):
return self.subject
class Someotherclass(models.Model):
relation = models.ForeignKey(
"Someclass", on_delete=models.CASCADE, null=True, related_name='myFkRelation')
detail = models.TextField()
date = models.DateField(auto_now_add=True, null=True)
This way you can connect the a cart with a user.
2: Create a (or 2) function(s) in your views.py where you can edit the data.
Example (using 2 separate functions):
# views.py
def change(request, id):
something = Someclass.objects.get(id=id)
context = {
'something': something,
}
return render(request, 'change.html', context)
def addchange(request, id):
change = request.POST['change']
something = Someclass.objects.get(id=id)
something.subject = change
something.date = datetime.now()
something.save()
return redirect('index')
----------
# urls.py
path('view/<int:id>/change/', views.change, name='change'),
path('view/<int:id>/change/addchange/', views.addchange, name='addchange'),
# of course you can change the urls to what you want as long as you give the id with it as a parameter to the views function.
----------
# change.html
<form action="addchange/" method="post">
{% csrf_token %}
<div class="form-group">
<label for="change">Change: *</label>
<textarea rows="4" cols="100" type="text" class="form-control" id="change" name="change" required></textarea>
</div>
<br>
<button type="submit" class="btn btn-primary">Submit</button>
</form>
3: Create a for loop within a HTML table (given the context in views, where a user has a relation with the cart)
Example:
# views.py
def index(request):
User = get_user_model()
users = User.objects.all()
context = {
'users': users
}
return render(request, "index.html", context)
----------
# index.html
<table class="table table-hover table-sm">
<thead>
<tr>
<th scope="col">Users</th>
<th scope="col">Items</th>
</tr>
</thead>
<tbody>
{% for user in users %}
<tr>
<td>{{user.name}}</td>
<td>{{user.item}}</td>
# or whatever you want
</tr>
{% endfor %}
</tbody>
</table>
Hopefully this answers some (maybe all?) of your questions.
Related
I am new to Django and trying to group and retrieve all expenses of the same category together and retrieve them in one "link like" table raw which when clicked can then display all the expenses in that category in another form.
I have these Models:
class Category(models.Model):
name = models.CharField(max_length=50)
class Meta:
verbose_name_plural = 'Categories'
unique_together = ("name",)
def __str__(self):
return self.name
class Expense(models.Model):
amount = models.FloatField()
date = models.DateField(default=now, )
description = models.TextField()
owner = models.ForeignKey(to=User, on_delete=models.CASCADE)
category = models.CharField(max_length=50)
def __str__(self):
return self.category
class Meta:
ordering = ['-date', '-pk']
homeView:
def home(request):
categories = Category.objects.all()
expenses = Expense.objects.filter(owner=request.user)
paginator = Paginator(expenses, 5)
page_number = request.GET.get('page')
page_obj = Paginator.get_page(paginator, page_number)
currency = UserPreference.objects.filter(user=request.user) # .currency
query = Expense.objects.values('category').annotate(total=Sum(
'amount')).order_by('category')
context = {
'expenses': expenses,
'page_obj': page_obj,
'currency': currency,
'query': query
}
return render(request, 'expenses/home.html', context)
mytemplate:
<div class="app-table">
<table class="table table-stripped table-hover">
<thead>
<tr>
<th>Amount</th>
<th>Category</th>
<th>Description</th>
<th>Date</th>
<th></th>
</tr>
</thead>
<tbody>
{% for myquery in query %}
<tr class="clickable-row" data-href="https://www.mavtechplanet.com/">
<td>{{myquery.category }}</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
I am trying to figure out things but are not coming out clearly. Any help is highly appreciated.
your template :
{% for myquery in query %}
<tr class="clickable-row" data-href="https://www.mavtechplanet.com/">
<td>{{myquery.category}}</td>
</tr>
{% endfor %}
add url to urls.py where in yourapp like this,
from youraapp.views import showCategoryExpenses
urlpatterns=[
.
.
.
path("/categoryExpenses/<pk:categoryPk>", showCategoryExpenses, name = "showCategoryExpenses"),
# if you save categories to expense with their name, you must change the <pk:categoryPk> to <slug:categoryName>
]
and add this view to yourapp.views like this:
def showCategoryExpenses(request, categoryPk): # if you save categories to expense with their name, change categoryPk to categoryName
expenses = Expense.objects.filter(category = categoryPk) # or category = categoryName
context = {
"expenses" : expenses
}
return render(request, "expenses/categoryExpenses.html", context)
and last, add this template to your template folder.. My English skills are not good but i try the solve and explain it. Maybe this is the answer you're looking for.
If you want show sum of all expenses in the category in this template, you can do it:
def showCategoryExpenses(request, categoryPk): # if you save categories to expense with their name, change categoryPk to categoryName
expenses = Expense.objects.filter(category = categoryPk) # or category = categoryName
expensesSum = 0
for expense in expenses:
expensesSum += expense.amount
context = {
"expenses" : expenses,
"expensesSum" : expensesSum
}
return render(request, "expenses/categoryExpenses.html", context)
Once you have categoryPk, you can try this way
from django.db.models import Sum
expenses = Expense.objects.filter(category = categoryPk).annotate(category_sum = Sum('amount')).values('category','category_sum')
# {'category':'ABC','category_sum':3000}
If you don't have any pk and want to annotate all objects then
expenses = Expense.objects.values('category').annotate(category_sum = Sum('amount')).values('category','category_sum')
I am working on an E-commerce project and I got lost in the middle to generate some kind of a report to get the total no. of a sold item after payment is made.
When the order is paid ordered = models.BooleanField(default=False) become True
I have tried to add the context with the total sold but it didn't work so I kept it in the code below but commented it.
Here is the Item models.py
class Item(models.Model):
title = models.CharField(max_length=100)
def __str__(self):
return self.title
Here is the OrderItemmodels.py
class OrderItem(models.Model):
ordered = models.BooleanField(default=False)
item = models.ForeignKey(Item, on_delete=models.CASCADE)
Here is the Order
class Order(models.Model):
items = models.ManyToManyField(OrderItem)
ordered = models.BooleanField(default=False)
Here is the views.py
class DesignerOnlyPostListView(ListView):
model = Item
template_name = "designer_only_posts.html"
context_object_name = 'items'
paginate_by = 6
def get_queryset(self):
user = get_object_or_404(User, username=self.kwargs.get('username'))
return Item.objects.filter(designer=user).order_by('-timestamp')
def get_context_data(self, **kwargs):
comments = Comment.objects.all()
# total_sold = Order.objects.all()
context = super().get_context_data(**kwargs)
context["total_comments"] = comments.count()
# context["total_sold"] = total_sold.count()
return context
Here is the template
{% for item in items %}
<tr>
<td>No. of Sold:</td>
<td>{{ item.total_sold.all.count }}</td>
</tr>
{% endfor %}
Seem in your template, you want to display total sold for each "item". In my opinion, i will add a method to Item, this approaching help you add custom filters as you want, something like this:
class Item(models.Model):
# ...
def count_sold(self):
return self.order_set.filter(ordered=True).count()
And in view:
<td>{{ item.count_sold }}</td>
Hope it will help you.
Im fairly new to Django. Im using Django 2.
My model:
# Create your models here.
class Trade(models.Model):
quantity = models.IntegerField()
open_price = models.FloatField()
commision = models.FloatField()
exchange_rate_usdsek = models.FloatField()
stock = models.ForeignKey(Stock, on_delete=models.CASCADE)
user = models.ForeignKey(User, related_name='trades', on_delete=models.PROTECT)
open_date = models.DateTimeField(auto_now_add=True, null=True)
def get_absolute_url(self):
return reverse('trade:detail',kwargs={'pk': self.pk})
def __str__(self):
return self.stock.name
My view
class IndexView(generic.ListView):
template_name = 'trade/index.html'
context_object_name = 'all_trades'
#
def get_queryset(self):
return Trade.objects.all()
#return Order.objects.all().prefetch_related('items')
def get_context_data(self, **kwargs):
context = super(IndexView, self).get_context_data(**kwargs)
return context
index.html
<h3>All stocks</h3>
<table class="table">
<thead>
<tr>
<th>Stock</th>
<th>Amount</th>
<th>Open Price</th>
<th>Commision</th>
<th>USD/SEK</th>
<th>Total sek</th>
</tr>
</thead>
{% for trade in all_trades %}
<tr>
<td>{{trade.stock.name}}</td>
<td>{{trade.quantity}}</td>
<td>{{trade.open_price}} USD</td>
<td>{{trade.commision}} SEK</td>
<td>{{trade.exchange_rate_usdsek}}</td>
<td>{{open_price * quantity * exchange_rate_usdsek}}</td>
</tr>
{% endfor %}
</table>
Now on my index.html I want to calculate and display a value.
I make a calculation like this:
total_sek = open_price * quantity * exchange_rate_usdsek
Do I have to calculate this in the views.py or in the index.html?
Also how would I do this? I searched around and found something about filters but im not sure if that is the right way to do it
The easiest way is to just define this calculation as a property on the model:
class Trade(models.Model):
# ...
#property
def total_sek(self):
return self.open_price * self.quantity * self.exchange_rate_usdsek
at which point you can simply
<td>{{ trade.total_sek }}</td>
in the template,
and trade.total_sek in Python code as required too.
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
Consider a model:
class TempReport(models.Model):
id = models.AutoField(primary_key=True)
cost = models.FloatField()
revenue = models.FloatField()
# Some other fields not relevant to topic
class Meta:
managed = False
db_table = 'temp_report'
unique_together = (('sale_point', 'date'), ('id', 'sale_point'),)
#property
def net_income(self):
return self.revenue - self.cost
My goal is to calculate net income = revenue - cost
The code for the template:
<tbody>
{% for repdata in reporttable %}
<tr>
<td> {{ repdata.revenue }}</td>
<td> {{ repdata.cost }}</td>
<td> {{ repdata.net_income}}</td>
</tr>
{% endfor %}
</tbody>
...and the view
def tempreport(request):
reporttable = TempReport.objects.values('id','cost','revenue')
return render_to_response('report.html',
{'reporttable': reporttable},
context_instance = RequestContext(request))
I end up with an empty net_income even if no error message is present. Any ideas why this might be caused by ?
Creating a property on the model should work. The indentation on your code is incorrect. The property should be a method of the model class, not the Meta class.
class TempReport(models.Model):
id = models.AutoField(primary_key=True)
cost = models.FloatField()
revenue = models.FloatField()
# Some other fields not relevant to topic
class Meta:
managed = False
db_table = 'temp_report'
unique_together = (('sale_point', 'date'), ('id', 'sale_point'),)
#property
def net_income(self):
return self.revenue - self.cost
In your view, don't use values(), because that will return dictionaries rather than model instances, and you won't be able to access the property.
from django.shortcuts import render
def tempreport(request):
reporttable = TempReport.objects.all()
for r in reporttable:
r.net_income = r.revenue - r.cost
return render(request, 'report.html', {'reporttable': reporttable})
Note I've also updated the view to use render instead of the obsolete render_to_response.