So when I place an order it creates a new order for every item in the cart. Lets say that I have 3 phones in my cart, then It would go to the database as as 3 orders. How would I go about it if I want to make it so these 3 phones would display as 1 order in "My orders"?
Here's my models:
class Order(TimestampMixin, db.Model):
id = db.Column(db.Integer, primary_key=True)
user_id = db.Column(db.Integer, db.ForeignKey('user.id'), unique=False )
phone_id = db.Column(db.Integer, db.ForeignKey('phone.id'),
unique=False, nullable=True)
accessory_id = db.Column(db.Integer, db.ForeignKey('accessory.id'),
unique=False, nullable=True)
That's code for displaying:
#phones.route("/myorders", methods=['GET', 'POST'])
#login_required
def myorder():
orders = Order.query.filter_by(user_id=current_user.get_id())
return render_template('phones/myorder.html', orders=orders)
and HTML:
{% extends "base.html" %}
{% block app_content %}
<div class="row">
<div class="container">
<div class="col-md-5">
<div class="content-section">
<table class="table table-striped">
{% for order in orders %}
<tr><th>{{order.id}} {{order.created_at}}</th>
<tr>
{% if order.phone %}
<td>{{order.phone.brand.name}} {{order.phone.model}} </td>
{% endif %}
{% if order.accessory %}
<td>{{order.accessory.brand}} {{order.accessory.compability.platform}} {{order.accessory.type.type_of_accessory}} </td>
{% endif %}
{% endfor %}
</table>
</div>
</div>
</div>
</div>
{% endblock %}
How it looks on the page:
Code for creating an order:
#phones.route("/order/", methods=['GET', 'POST'])
#login_required
def order():
list_of_phones = session['cart']
list_of_accessories = session['cart2']
for pid in list_of_phones:
phone = Phone.query.get(pid)
phone.stock = phone.stock - 1
order = Order(
user_id = current_user.get_id(),
phone_id = phone.id)
db.session.add(order)
db.session.commit()
for aid in list_of_accessories:
accessory = Accessory.query.get(aid)
accessory.stock = accessory.stock - 1
order = Order(
user_id = current_user.get_id(),
accessory_id = accessory.id)
db.session.add(order)
db.session.commit()
session.pop('cart')
session.pop('cart2')
flash('Order was added successfully', 'success')
return redirect(url_for('phones.shopping_cart'))
Difference after changes:
I modified code for displaying all orders:
#phones.route("/myorders", methods=['GET', 'POST'])
#login_required
def myorder():
# orders = Order.query.filter_by(user_id=current_user.get_id())
orders = db.session.query(Order).filter_by(user_id = current_user.id).group_by(Order.order_id).all()
print(orders)
return render_template('phones/myorder.html', orders=orders)
And it kind of works, because it only display one order_id, but on the other side it only displays one item, see below:
I tested this code, it works and groups order by randomly generated order_id:
#phones.route("/order/", methods=['GET', 'POST'])
#login_required
def order():
list_of_phones = session['cart']
random_string = random_generator()
for pid in list_of_phones:
phone = Phone.query.get(pid)
phone.stock = phone.stock - 1
order = Order(
user_id = current_user.get_id(),
phone_id = phone.id)
order_id = random_string
db.session.add(order)
db.session.commit()
session.pop('cart')
flash('Order was added successfully', 'success')
return redirect(url_for('phones.shopping_cart'))
def random_generator():
size = 16
chars = string.ascii_letters + string.digits
return ''.join(random.choice(chars) for _ in range(size))
in your app route:
#phones.route("/myorders", methods=['GET', 'POST'])
#login_required
def myorder():
orders = db.session.query(Order).filter_by(
user_id = current_user.id).all()
return render_template('phones/myorder.html',
orders=orders)
In your template, you can use something like the following:
{% for order_id, order in orders | groupby('order_id') %}
{{ order_id }}
{% for i in order %}
<ul>
{{ i.id }}
{{ i.phone_name }}
</ul>
{% endfor %}
<br>
{% endfor %}
with some test data I added to database, I get the following
abc
1 phone1
2 phone2
3 phone3
xyz
4 phone4
5 phone5
6 phone6
Related
I am trying to insert a new customer into my datatable and I keep getting 400 Bad Request, "The browser (or proxy) sent a request that this server could not understand." Here is what I get on the terminal, "POST /customer_insert HTTP/1.1" 400 -"
No matter what I do, nothing helps.
Route to "Insert Customer form"
#app.route('/customer_insert', methods=['GET', 'POST'])
def customer_insert():
form = Customer()
if form.validate_on_submit():
result = request.form
customer = pk_db.Customer(id = result["id"], name = result["name"], bill = result["bill"], date = result["date"])
session.add(customer)
session.commit()
return render_template('customer_insert_after.html', title = "New Customer", header = "New Customer", result = result)
return render_template('customer_insert.html', title = "Insert Customer Form", header = "Insert Customer Form", form = form)
Sqlalchemy customer class:
class Customer(Base):
__tablename__ = 'customer'
id = Column(Integer, primary_key=True)
name = Column(String)
bill = Column(Float)
date = Column(String)
bought = relationship("Product", secondary = customers_and_products, viewonly=True)
Relational table to the Product table:
customers_and_products = Table('customers_and_products', Base.metadata,
Column('customer_id', Integer, ForeignKey('customer.id')),
Column('product_id', Integer, ForeignKey('product.id'))
)
Flask WTF forms
class Customer(FlaskForm):
name = StringField("Full name",
validators = [InputRequired(message = "You must enter full name"),
Length(min = 2, max = 60, message="Name length must be between 2 and 60 characters")])
bill = FloatField("Bill amount",
validators = [InputRequired(message = "You must enter bill amount")])
date = StringField("Date of purchase",
validators = [InputRequired(message = "You must enter date")])
submit = SubmitField("Insert Customer")
"customer_insert.html" file
{% extends "datatable_layout.html" %}
{% block title %} {{title}} {% endblock title %}
{% block header %} {{header}} {% endblock header %}
{% block content %}
<form action="" method="post">
{{ form.csrf_token }}
<div>
{{form.name.label}}
<span class="error">{{ form.name.errors | join("|") }}</span>
</div>
<div>
{{form.name}}
</div>
<div>
{{form.bill.label}}
<span class="error">{{ form.bill.errors | join("|") }}</span>
</div>
<div>
{{form.bill}}
</div>
<div>
{{form.date.label}}
<span class="error">{{ form.date.errors | join("|") }}</span>
</div>
<div>
{{form.date}}
</div>
<!-- SUBMIT -->
<div>
{{form.submit()}}
</div>
</form>
{% endblock content %}
"customer_insert_after.html" file
{% extends "datatable_layout.html" %}
{% block title %} {{title}} {% endblock title %}
{% block header %} {{header}} {% endblock header %}
{% block content %}
Record of inserted customer
Customer name: {{result["name"]}}
Bill: {{result["bill"]}}
Date: {{result["date"]}}
{% endblock content %}
models.py ( considering all required data ) how to get only logged in customer but it is showing all Customer
class Customer(models.Model):
first_name = models.CharField(max_length=50)
last_name = models.TextField(max_length=50)
phone = models.CharField(max_length=15)
email = models.EmailField(max_length=100 ,default='')
password = models.CharField(max_length=500)
views.py ( considering all required done )
def profile(request):
data= Customer.objects.all()
return render(request,'profile.html' ,{'data':data})
profile.html ( considering making urls) and all doing requirement
{% if data %}
{% for d in data %}
<h2>{{ d.email }}</h2>
{% endfor %}
{% endif %}
data= Customer.objects.all()
data is a list of Customers not a single Customer, so there is no email attribute available. Calling data.email in the template returns None.
You need to loop through data in the template.
{% if data %}
{% for d in data %}
<h2>{{ d.email }}</h2>
{% endfor %}
{% endif %}
I need to limit the number of posts in Django queries. I have tried to add a min and max but nothing seemed to have worked. I have added home.html into the code.
Example: I should only have the 15 most recent posts in my blog. The rest can be seen by clicking on the category button.
Home.html:
{% extends 'base.html' %}
{% block content %}
<h1>Posts</h1>
<ul>
{% for post in object_list %}
<li>{{post.title}}
<style>
a {
text-transform: capitalize;
}
</style>
- {{ post.category }} - <a href="{% url 'show_profile_page' post.author.profile.id %}">{{ post.author.first_name }}
{{ post.author.last_name }}</a> - {{ post.post_date }} <small>
{% if user.is_authenticated %}
{% if user.id == post.author.id %}
- (Edit)
(Delete)
{% elif user.id == 1 %}
- (Edit)
(Delete)
{% endif %}
{% endif %}
</small><br/>
{{ post.snippet }}</li>
{% endfor %}
</ul>
{% endblock %}
view.py:
class HomeView(ListView):
model = Post
template_name = 'home.html'
ordering = ['-id']
def get_context_data(self, *args, **kwargs):
cat_menu = Category.objects.all()
context = super(HomeView, self).get_context_data(*args,**kwargs)
context["cat_menu"] = cat_menu
return context
models.py:
class Post(models.Model):
title = models.CharField(max_length=255)
header_image = models.ImageField(null=True, blank=True, upload_to='images/')
title_tag = models.CharField(max_length=255)
author = models.ForeignKey(User, on_delete=models.CASCADE)
body = RichTextField(blank=True, null=True)
post_date = models.DateField(auto_now_add=True)
category = models.CharField(max_length=255, default='intro')
snippet = models.CharField(max_length=255)
likes = models.ManyToManyField(User, related_name='post_likes')
dislikes = models.ManyToManyField(User, related_name='post_dislikes')
I think you have another template for displaying categorised objects when you click category button. As you said
"I should only have the 15 most recent posts in my blog. The rest can
be seen by clicking on the category button."
In this case you can use a simple hack to display most recent posts from your table.
query all objects in descending order in views
all_objs = Post.objects.all().order_by('-id')
Then use {% if forloop.counter <= 15 %} to display last 15 items only. as follow.
templates
{% for post in object_list %}
{% if forloop.counter <= 15 %}
<h4>{{obj}} #or anything really that is meant to be shown on the home page.</h4>
{% endif %}
{% endfor %}
You can do something like this:
def get_context_data(self, *args, **kwargs):
context = super(HomeView, self).get_context_data(*args,**kwargs)
context["cat_menu"] = Category.objects.all()
context["most_recent_posts"] = Post.objects.filter(author=self.request.user).order_by('-post_date')[:15]
return context
This will get the 15 most recent posts authored by the current user, ordered by the date it was posted.
Then just handle displaying this in home.html for example:
<ul>
{% for p in most_recent_posts %}
<li>{{ p.title }}</li>
{% endfor %}
</ul>
Just limit your query to the latest 15 entries sorted by post_date:
cat_menu = Category.objects.latest("post_date")[:15]
https://docs.djangoproject.com/en/3.2/topics/pagination/
The best way is Django Pagintion.
{% for contact in page_obj %}
{# Each "contact" is a Contact model object. #}
{{ contact.full_name|upper }}<br>
...
{% endfor %}
<div class="pagination">
<span class="step-links">
{% if page_obj.has_previous %}
« first
previous
{% endif %}
<span class="current">
Page {{ page_obj.number }} of {{ page_obj.paginator.num_pages }}.
</span>
{% if page_obj.has_next %}
next
last »
{% endif %}
</span>
</div>
from django.core.paginator import Paginator
from django.shortcuts import render
from myapp.models import Contact
def listing(request):
contact_list = Contact.objects.all()
paginator = Paginator(contact_list, 25) # Show 25 contacts per page.
page_number = request.GET.get('page')
page_obj = paginator.get_page(page_number)
return render(request, 'list.html', {'page_obj': page_obj})
you can use Django pagination api . Manage your data through page number. Initially pass 1 and after that page number given by pagination.
paginator = Paginator(yourquerysetdata, 20)
page_num = request.data.get('page')
result = yourSerializerName(paginator.get_page(page_num) many=True).data
try:
page = paginator.page(page_num)
except:
page = paginator.page(1)
count = paginator.num_pages
resultobj = paginator.get_page(page_num)
has_prev = resultobj.has_previous()
has_next = resultobj.has_next()
page_range = resultobj.paginator.page_range.stop - 1
if has_prev:
prev_page_no = resultobj.previous_page_number()
else:
prev_page_no = 0
if has_next:
next_page_no = resultobj.next_page_number()
else:
next_page_no = None
context = dict()
context['page'] = page.number
context['page_no'] = count
It is very simple. You just have to modify the query that you are using to fetch the posts.
In the get_context_data() method, replace cat_menu = Category.objects.all() with cat_menu = Category.objects.all().order_by('-post_date')[:15]. This will limit the number of results to 15 most recent objects.
For more understanding, you can take a look at the official Django docs for Limiting QuerySets.
I've been able to display all offices in an election, but have been unable to display all candidates in an office on the same page
models.py
class Election(models.Model):
name = models.CharField(max_length = 30)
slug = models.SlugField(max_length = 250, null = False, unique = True)
class Office(models.Model):
election = models.ForeignKey(Election, on_delete=models.CASCADE, default=None)
name = models.CharField(max_length = 30)
class Candidate(models.Model):
office = models.ForeignKey(Office, on_delete=models.CASCADE, default=None)
name = models.CharField(max_length = 30)
views.py
def poll(request, id):
context = {
'election' : Election.objects.get(pk=id),
'off' : Office.objects.all()
}
return render(request, 'poll.html', context)
poll.html
{% for office in election.office_set.all %}
<div class="text-center">
<h3>{{ office.name }}</h3>
{% for candidate in off.candidate_set.all %}
<h5>{{ candidate.name }}</h5>
{% endfor %}
</div>
{% endfor %}
In your first line of poll.html, you call "office" from the queryset "election.office_set.all"
Therefore, when you call a subquery of that QS, you have to reference the original name, ie: office, rather than off.
Your final code should look like this:
poll.html
{% for office in election.office_set.all %}
<div class="text-center">
<h3>{{ office.name }}</h3>
{% for candidate in office.candidate_set.all %}
<h5>{{ candidate.name }}</h5>
{% endfor %}
</div>
{% endfor %}
You also don't need the line
'off' : Office.objects.all()
in your Views.py.
I have a query on my views.py:
query = db.session.query(Basket, Fruits).filter(Fruits.basket_id == Basket.id)
In the html template, I wish to display for each basket id (basket id here is unique), what are the fruits inside each basket, thus I used a nested for loop in my template.
I have tried on the html template:
{% for basket in query | unique %}
<p>{{ basket.Basket.id }}</p>
{% for fruits in query %}
<p>{% if basket.Basket.id == fruits.Basket.id %}
<p>{{ query.Fruits.fruit }}</p>
{% endif %}
{% endfor %}
But the unique basket id is not displayed.
I have thought of creating 2 queries so that I can put .distinct and display the unique basket id, then use the other query to display the fruits, but that didn't make sense to me because I still need all the information in the whole query. Beginner in flask btw.
You might be able to achieve that with a one to many relationship.
class Basket(db.Model):
id = db.Column(db.Integer, primary_key = True)
name = db.Column(db.String(100))
fruits = db.relationship('Fruit', backref='basket')
class Fruit(db.Model):
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(100))
basket_id = db.Column(db.Integer, db.ForeignKey('basket.id'))
#app.route('/')
def baskets():
baskets = db.session.query(Basket).all()
return render_template('baskets.html', baskets=baskets )
Jinja2 Template:
{% for b in baskets %}
<ul>
<p>
{% b.name %}
</p>
{% for f in b.fruits %}
<li>{{ f.name }}</li>
{% endfor %}
</ul>
{% endfor %}