multiple tables are mapped and, when I create post request,
it takes about 2~3 seconds. Is there any ways to fix it?
I guess it takes a long time on:
objects.create
for loop
product.objects.get
however, I am not able to find the better ways..
models:
#product, Order, OrderItems, ShippingAddress are mapped
class Order(models.Model):
user = models.ForeignKey(User, on_delete= models.CASCADE)
order_date = models.DateTimeField(auto_now=True)
is_paid = models.BooleanField(default=False)
paid_at = models.DateTimeField(auto_now=False, null=True, blank=True)
delivery_code = models.CharField(max_length=255, null=True, blank=True)
is_delivered = models.BooleanField(default=False)
delivered_date = models.DateTimeField(auto_now=False, null=True, blank=True)
total_price = models.DecimalField(max_digits=7, decimal_places=2, null=True)
shipping_price = models.DecimalField(max_digits=7, decimal_places=2, null=True)
payment_method = models.CharField(max_length=255,null=True)
def __str__(self):
return str(self.user)
class OrderItem(models.Model):
order = models.ForeignKey(Order, on_delete= models.CASCADE, null=True, blank=True)
product = models.ForeignKey(Product, on_delete= models.CASCADE)
name = models.CharField(max_length=200, null=True)
image = models.CharField(max_length=255, null=True)
qty = models.IntegerField(default=0, null=True)
price = models.DecimalField(max_digits=7, decimal_places=2, null=True)
def image_preview(self):
if self.image:
return mark_safe('<img src="{0}" width="55" height="55" />'.format(self.image))
else:
return '(No image)'
def __str__(self):
return str(self.product)
class ShippingAddress(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE)
order = models.OneToOneField(Order, on_delete=models.CASCADE, null=True, blank=True)
address = models.CharField(max_length=255, null=False)
city = models.CharField(max_length=255, null=False)
postal_code = models.CharField(max_length=255, null=False)
country = models.CharField(max_length=255, null=False)
def __str__(self):
return str(self.user)
view:
#permission_classes(IsAuthenticated)
#api_view(['POST'])
def OrderCreate(request):
data = request.data
user = request.user
order_items = data['orderItems']
#1.create order
order = Order.objects.create(
user = user,
total_price = data['totalPrice'],
shipping_price = data['shippingPrice'],
payment_method = data['paymentMethod']
)
#2.create orderItems
for i in order_items:
product = Product.objects.get(id=i['id'])
order_item = OrderItem.objects.create(
order = order,
product = product,
name = i['name'],
qty = i['qty'],
price = i['price'],
image = i['image']
)
#3. update stock
product.stock -= i['qty']
product.save()
#4.create shipping address
shipping_address = ShippingAddress.objects.create(
user = user,
order = order,
address = data['shippingAddress']['address'],
city = data['shippingAddress']['city'],
postal_code = data['shippingAddress']['postalCode'],
country = data['shippingAddress']['country'],
)
#5.serializing and save
serializer = OrderSerializer(order, many=False)
return Response(serializer.data)
You can instantiate the order_items without ever fetching the product, provided you have sufficient trust for the product ids in i['id']
for i in order_items:
# product = Product.objects.get(id=i['id'])
order_item = OrderItem.objects.create(
order = order,
product_id = i['id'], # set the id (magic suffix) without fetching product
name = i['name'],
qty = i['qty'],
price = i['price'],
image = i['image']
)
Instead of using .create you might instantiate these order_items as a list of unsaved instances and create them using OrderItem.bulk_create Read the bulk_create documentation; it has a number of caveats.
You could then run a loop updating the product stock field using an F expression to subtract from the current value in the product row without actually fetching a product object from the DB
for i in order_items:
product_id = i['id']
Product.objects.filter(
pk = product_id
).update(
stock = F('stock') - i['qty']
)
If you do fetch all the product instances into a list with updated stock values, there's also bulk_update which would let you apply all the updated stock values in a single DB operation. This might be better than doing them one by one with an F expression. You can also fetch them in bulk using
Product.objects.filter( pk__in=[ i['id'] for i in order_items ] )
(Warning, I don't think that there's any guarantee that the queryset contains the objects in the same order that you supply the i['id'] values )
Treat this as brainstorming. I'm not entirely certain that this is correct and I really don't know whether it will speed things up a lot, a little, or at all. I'd be interested to know, if you try it.
Related
Hi I have models that are contectet via foregin key. I can have multiple orders so let's say that i have 3 orders. 2xTshirt 1xPants and 4xHats. How can I acces each product and change the stock of them based on quantity of an order.
views
order = Order.objects.get_or_create(customer=customer, complete=False)
order_items = OrderItem.objects.filter(order=order)
for item in order_items:
item.product.stock = int(item.product.stock) - int(item.quantity)
item.transaction_id = transaction_id
item.save()
models
class Product(models.Model):
title = models.CharField(max_length=70, null=True, blank=True)
producent = models.CharField(max_length=40, null=True, blank=True)
stock = models.PositiveIntegerField(null=True, blank=True)
class OrderItem(models.Model):
product = models.ForeignKey(Product, on_delete=models.SET_NULL, blank=True, null=True)
quantity = models.IntegerField(default=0, null=True, blank=True)
You are not saving the product object. Try this:
for item in order_items:
product = item.product
product.stock = product.stock - item.quantity
product.save()
item.transaction_id = transaction_id
item.save()
I'm creating a small web application to track my orders, i have two models:
Order(models.Model) " This model to record the new orders"
class Order(models.Model):
customer= models.ForeignKey(Customer, null=True, on_delete= models.SET_NULL)
product= models.ForeignKey(Product, null=True, on_delete= models.SET_NULL)
date_created = models.DateTimeField(auto_now_add=True, null=True)
status = models.CharField(max_length=200, null=True, choices=CATEGOTRIES)
note = models.CharField(max_length=200, null=True)
OrderHistory(models.Model) " This model to record the changes for each order in any field"
class OrderHistory(models.Model):
version_num = models.AutoField(primary_key=True)
Order = models.ForeignKey(Order, null=True, on_delete= models.SET_NULL)
customer= models.ForeignKey(Customer, null=True, on_delete= models.SET_NULL)
product= models.ForeignKey(Product, null=True, on_delete= models.SET_NULL)
date_created = models.DateTimeField(auto_now_add=True, null=True)
status = models.CharField(max_length=200, null=True)
note = models.CharField(max_length=200, null=True)
View:
def update_orders(request,pk):
theOrder = Order.objects.get(id=pk)
theHistory = createOrderHistory({
'Order': theOrder.id,
'customer': theOrder.customer,
'product': theOrder.product,
'date_created': theOrder.date_created,
'status': theOrder.status,
'note': theOrder.note
})
print('The History of the order ',theHistory)
UpdateForm = createOrder(instance=theOrder)
theid = pk
if request.method == 'POST':
UpdateForm = createOrder(request.POST,instance=theOrder)
if theHistory.is_valid():
if UpdateForm.is_valid():
theHistory.save()
UpdateForm.save()
return redirect('home')
else:
UpdateForm = createOrder(instance=theOrder)
context = {'UpdateForm':UpdateForm}
return render(request,'accounts/updateOrder.html',context)
In the View update_orders function am taking the current order and save it into OrderHistory and
then i save the new changes into Order Model
it's working fine but the OrderHistory Model is not including the new change.
orderHistory to get the history of the order
def orderHistory(request,pk):
theHistoryOfTheOrder = Order.objects.filter(id=pk).prefetch_related('Order')
#theHistoryOfTheOrder = Order.objects.filter(id=pk)
print('the query result is :',theHistoryOfTheOrder)
context = {'theHistoryOfTheOrder':theHistoryOfTheOrder}
return render(request,'accounts/orderHistory.html',context)
i tried to use prefetch_related() since i'm fetching multiple records but it's through an error
Cannot find 'Order' on Order object, 'Order' is an invalid parameter to prefetch_related()
i need to get all the records in Order and OrderHistory with the same orderID
also is this the right solution to preform History or is there another good way to do it?
I have seen a lot of posts on how to deserialize and serialize nested relationships of tables having a many to many relationship, but when an intermediate table is used in a many to many relationship I am not able to achieve deserialization.
This is because an intermediate table requires two foreign keys, one each from the two tables participating in the relation.
I have an Order model and a Service model which are in a many to many relationship via an OrderItem intermediate table.
I need to pass a JSON request like this:
{"service_time":"2015-11-14 10:00:51+0530",
"address":"xyz",
"items":[{"order": "1", "service_id":"4"},
{"order":"1", "service_id":"5"}]
}
The "service_time" and "address" elements get saved in the Order table. Now the problem arises with the "items" JSON array. I pass the "service_id" (foreign key to the Service table) and I need to pass "order" (foreign key to the Order table) too as it is a required field. The problem is that the primary key of the Order table is not known when the request is sent(as the Order is also created as a part of the same request). How can I achieve deserialization in this scenario?
I tried something like this but it didn't work out.
class OrderSerializer(serializers.ModelSerializer):
items = ItemSerializer(many=True)
class Meta:
model = Order
def create(self, validated_data):
items_data = validated_data.pop('items')
orders = Order.objects.create(**validated_data)
for item in items_data:
#order = item['order']
service = item['service']
//Passing the Order object just created as the foreign key of OrderItem
orderItem = OrderItem.objects.create(order=orders, service=service)
orderItem.save()
return orders
class ServiceSerializer(serializers.ModelSerializer):
group = serializers.CharField(source="group.group_name")
category = serializers.IntegerField(source="group.category_id")
class Meta:
model = Service
fields = ['id', 'service_name', 'price', 'special_price', 'service_time', 'group', 'category']
class ItemSerializer(serializers.ModelSerializer):
service_detail = ServiceSerializer(source="service", read_only=True)
class Meta:
model = OrderItem
I get an error saying 'Service' object has no attribute 'order'.
I know the Service model does not have an "order" attribute, but I am creating an OrderItem object, not a Service object.
Any suggestions will be helpful!
Edit: Adding the models used
class Order(models.Model):
STATUSES = [('PENDING', 'Pending'), ('PROCESSING', 'Processing'), ('COMPLETE', 'Complete'), ('CANCELED', 'Canceled')]
PAYMENT_STATUSES = [('PENDING', 'Pending'), ('PAID', 'Paid'),]
CANCEL_REASON = [('NO_SERVICE', 'No Service In Area'), ('NO_STYLIST', 'Stylist Not Available'), ('STYLIST_REFUSED', 'Stylist Refused After Accepting',),
('CUSTOMER_UNREACHABLE', 'Customer Not Reachable'), ('CUSTOMER_CANCELED', 'Customer Canceled at Fisrt Call'), ('CUSTOMER_REFUSED', 'Customer Refused at Last Moment'),
('DUPLICATE_ORDER', 'Duplicate Order'), ('MALE_CLIENT', 'Male Client'), ('CALLCENTER_DELAY', 'Delay/Error at Frontdesk')]
serial = models.CharField(max_length=10, null=True, blank=True,)
customer = models.ForeignKey(Customer, verbose_name="customer", related_name="ordersbycustomer")
stylist = models.ForeignKey(Stylist, null=True, blank=True, verbose_name="stylist", related_name="ordersbystylist")
# TODO, Use timezone.now
service_time = models.DateTimeField(default=timezone.now, blank=True)
started_moving = models.DateTimeField(null=True, blank=True)
service_start_at = models.DateTimeField(null=True, blank=True)
service_end_at = models.DateTimeField(null=True, blank=True)
reached_safely = models.DateTimeField(null=True, blank=True)
sub_total = models.FloatField(default=0)
discounts = models.FloatField(default=0)
grand_total = models.FloatField(default=0)
total_time = models.IntegerField(default=0)
status = models.CharField(max_length=32, choices=STATUSES, default='PENDING')
payment_status = models.CharField(max_length=32, choices=PAYMENT_STATUSES, default='PENDING')
items = models.ManyToManyField(Service, through='OrderItem')
address = models.ForeignKey(Address, null=True, blank=True, related_name='+', on_delete=models.SET_NULL)
amount_received = models.FloatField(default=0)
send_sms = models.BooleanField(default=True)
thru_app = models.BooleanField(default=True)
referral_discount = models.FloatField(default=0)
cancellation_reason = models.CharField(max_length=64, choices=CANCEL_REASON, null=True, blank=True)
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
def __unicode__(self):
return self.serial
def _get_service_list(self):
return ','.join(str(p.description) for p in self.items.all())
service_list = property(_get_service_list)
class Service(models.Model):
group = models.ForeignKey(Group, related_name="services")
service_name = models.CharField(max_length=128)
price = models.FloatField(default=0)
special_price = models.FloatField(default=0)
service_time = models.IntegerField()
description = models.CharField(max_length=123)
is_active = models.BooleanField(default=True)
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
def __unicode__(self):
return '{} ({})'.format(self.service_name, self.group)
class OrderItem(models.Model):
order = models.ForeignKey(Order)
service = models.ForeignKey(Service)
price = models.FloatField(default=0)
special_price = models.FloatField(default=0)
qty = models.IntegerField(default=1)
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
def __unicode__(self):
return self.service.service_name
Edit2: Added the other related serializers.
One important thing I forgot to mention is that, the data gets saved in the DB, but the exception is still raised.
I've been following the manual for generic views for Django 1.4, but can get the 'list books by publisher' example to work. My site is slightly different in that I'm trying to list bookings of a property by the name (or id) of the person who books the property. People will book more than once, so I want to be able to see what their bookings were.
My views.url for this is:
class GuestBookingListView(DetailView):
context_object_name = 'guest_booking'
template_name = 'guest_booking.html'
def get_queryset(self):
self.guest = get_object_or_404(Guest)
return Booking.objects.filter(guest = self.guest)
def get_context_data(self, **kwargs):
context = super(GuestBookingListView, self).get_context_data(**kwargs)
context['guest'] = self.guest
return context
My model is:
class Guest(models.Model):
first_name = models.CharField(max_length=30)
last_name = models.CharField(max_length=50)
spouse_first = models.CharField(max_length=30, blank=True)
spouse_last = models.CharField(max_length=50, blank=True)
num_child = models.IntegerField(verbose_name='Number of children')
address = models.TextField(max_length=50, blank=True)
city = models.CharField(max_length=60, blank=True, verbose_name='Town / City')
state_province = models.CharField(max_length=30, blank=True, verbose_name='County')
post_code = models.CharField(max_length=8, blank=True)
country = models.CharField(max_length=50, blank=True)
email = models.EmailField(blank=True)
landline = models.CharField(max_length=25, blank=True)
mobile = models.CharField(max_length=25, blank=True)
def __unicode__(self):
return u'%s %s' % (self.first_name, self.last_name)
class Booking(models.Model):
guest = models.ForeignKey(Guest)
ack_date = models.DateField(verbose_name='Date acknowledged')
start_date = models.DateField(verbose_name='Start date')
end_date = models.DateField(verbose_name='End date')
dep_recd = models.DateField(null=True, blank=True, verbose_name='Deposit received')
bal_recd = models.DateField(null=True, blank=True, verbose_name='Balance received')
keys_sent = models.DateField(null=True, blank=True, verbose_name='Date keys sent')
sec_retn = models.DateField(null=True, blank=True, verbose_name='Security deposit returned')
rtm_sent = models.IntegerField('Status', blank=True)
notes = models.TextField(blank=True, verbose_name='Notes')
and my urls.py is:
url(r'^guests/(?P<pk>\d+)/$', GuestBookingListView.as_view (
#context_object_name = 'booking_list',
)),
So far as I can see this identical (with different field names) to the example, but the result I get is:
get() returned more than one Guest -- it returned 26! Lookup parameters were {}
The 'get' is retrieving all of the Guests in the database, not the one which I've selected.
I've spent hours of searching and experimenting on this, but to no avail. If I put 'guest = 11' it works, so there's something wrong with the pk.
Thank you!
You haven't given any sort of criteria to get the guest. You've just said, in effect, "give me guest", and Django has given you all 26 of them. If you want to filter by the pk kwarg, you should say so:
self.guest = get_object_or_404(Guest, pk=self.kwargs['pk'])
I am working on a project and a student of Web Development.
I am making an application in Django. When a user creates a new trip with a form I've made, I want to add a map to that form so that users only have to click a point on a map in order to get coordinates to save to the database for that trip.
I want to save coordinates for every new trip so that I can render a map on each user's profile page that shows them markers of every trip they've taken that is in the database.
Please help!
Thank you
Here's some code:
models.py
class Trip (models.Model):
user = models.ForeignKey(User, on_delete = models.CASCADE, related_name='trips')
start_date = models.DateField()
end_date = models.DateField()
trail = models.CharField(max_length=300)
permit = models.URLField(blank=True, null=True)
completed = models.BooleanField(default=False)
location = models.CharField(blank=True, max_length=400)
lat = models.DecimalField(blank=True, null=True, max_digits=9, decimal_places=6)
lng = models.DecimalField(blank=True, null=True, max_digits=9, decimal_places=6)
created_at = models.DateField(auto_now_add =True)
def __str__ (self):
return self.trail
def total_days(self):
return (self.end_date - self.start_date).days
forms.py
class CreateTrip(forms.ModelForm):
start_date = forms.DateField(
widget=forms.TextInput(
attrs={'type': 'date'}
)
)
end_date = forms.DateField(
widget=forms.TextInput(
attrs={'type': 'date'}
)
)
class Meta():
model = models.Trip
fields = ('trail','location', 'permit', 'completed', 'start_date', 'end_date')
Sounds like to me you have a couple of many-to-one relationships you want to store in your database. My answer is based on this documentation:
https://docs.djangoproject.com/en/2.1/topics/db/examples/many_to_one/
You have Users that can go on many trips.
So you'd have a model called Trip that would look something like:
class Trip(models.Model):
name = models.CharField(max_length=100)
create_date = models.DateTimeField(auto_now_add=True)
user = models.ForeignKey(User)
... whatever else ...
And your trips have points so you might want a model like this:
class Point(models.Model):
lat = models.DecimalField(blank=True, null=True, max_digits=9, decimal_places=6)
lng = models.DecimalField(blank=True, null=True, max_digits=9, decimal_places=6)
order = models.IntegerField() # ordering for the points.
create_date = models.DateTimeField(auto_now_add=True)
trip = models.ForeignKey(Trip)
... whatever else ...