Failed to create a seller instance using django signals - django

I'm building a website with 2 user types and still new to django.
And I want to add the functionality to add the seller of the product whenever a product is sold.
I'm sorry that I couldn't explain it better.
Here's the code of models.py:
class Ordered(models.Model):
products = models.ForeignKey(Products, on_delete = models.SET_NULL, null = True)
seller = models.ForeignKey(SellerProfile, on_delete = models.SET_NULL, null = True)
buyer = models.ForeignKey(CustomerProfile, on_delete = models.CASCADE)
ordered_on = models.DateTimeField(auto_now_add = True)
product/models.py
class Products(models.Model):
seller = models.ForeignKey(SellerProfile, on_delete = models.CASCADE)
title = models.CharField(max_length = 255)
product_category = models.CharField(choices = CATEGORY_CHOICES, max_length = 100, default = 'eBooks')
description = models.TextField()
files = models.FileField(upload_to = 'media/product_files/', null = True)
slug = models.SlugField(max_length = 255, unique = True, null = True, blank = True)
And this is the signal code:
#receiver(post_save, sender = Ordered)
def new_order_for_seller(sender, instance, created, *args, **kwargs):
seller = Ordered.seller.sellerprofile
if created:
Ordered.objects.create(seller = seller)
Any suggestion or correction of the code will be really helpful.
Thank you

You can set the seller attribute as the instance.product.seller:
#receiver(pre_save, sender = Ordered)
def new_order_for_seller(sender, instance, created, *args, **kwargs):
if created and instance.product is not None:
instance.seller_id = instance.product.seller_id
We can do this in a pre_save signal to prevent saving the new Ordered object a second time.
That being said, since the seller is already determined by the Product, it does not make much sense to duplicate this, since it can eventually lead to inconsistencies where the Seller of a Product changes later, and the Ordered is still pointing to the "old" Seller.

Related

slow process during saving model in database

i want save a list of model in database table but i very slow
when i use save() method for each item it took near 20min
is that a best way to save objects to table
Modles.py
class Part(models.Model):
block = models.CharField(max_length= 2, null= True)
phase = models.CharField(max_length= 3, null= True)
department = models.CharField(max_length= 20, null= True)
type = models.CharField(max_length= 10, null= True)
mark = models.CharField(max_length= 20, null= True)
class Task(models.Model):
name = models.CharField(max_length= 20)
class ProjectTask(models.Model):
project = models.ForeignKey('Project', on_delete= models.CASCADE)
task = models.ForeignKey("Task", on_delete=models.CASCADE)
weight_percent = models.FloatField()
class PartTask(models.Model):
part = models.ForeignKey('Part', on_delete= models.CASCADE)
project_task = models.ForeignKey('ProjectTask', on_delete= models.CASCADE)
progress = models.FloatField(null=True)
views.py
def import_part_task(_project_id):
project_id = _project_id
project_task = ProjectTask.objects.all().filter(project= int(project_id[0]))
part_list = Part.objects.all()
part_task_list = []
for part in part_list:
for task in project_task:
part_task = PartTask()
part_task.part =part
part_task.project_task = task
part_task_list.append(part_task)
#This ACTION TAKES VERY LOG TIME
for part_task in part_task_list:
PartTask.save(part_task)
That makes perfect sense, since saving the database means that you each time query the database. This takes significant time.
You can however boost performance by inserting with bulk_create(..) [Django-doc]:
def import_part_task(_project_id):
project_id = _project_id
project_task = ProjectTask.objects.filter(project= int(project_id[0]))
part_list = Part.objects.all()
part_task_list = [
PartTask(part=part, project_task=task)
for part in part_list
for task in project_task
]
PartTask.objects.bulk_create(part_task_list)
By inserting in bulk, Django will create a query to insert a large amount of objects with a single query, instead of each time making a query for each individual PartTask object. The amount of "round trips" to the database is thus reduced significantly.

Django creates new cart-product instead of updating already existing object

In my django shop I have a adding to cart function. But if I add the same product 2 times to the cart with a different quantity, 2 different objects are created. What's wrong with my code?
here is my view
def add_to_cart_view(request):
cart = getting_or_creating_cart(request)
product_slug = request.POST.get('product_slug')
product = Product.objects.get(slug=product_slug)
if request.method == "POST":
form = CartAddProductForm(request.POST or None)
if form.is_valid():
quantity = form.cleaned_data['quantity']
new_item, created = CartItem.objects.get_or_create(
product=product,
item_cost=product.price,
quantity=quantity,
all_items_cost=product.price*quantity,
)
if new_item.product.title == product.title:
cart.items.add(new_item)
cart.save()
if not created:
new_item.quantity += quantity
new_item.save(force_update=True)
cart.save()
new_cart_total = 0.00
for item in cart.items.all():
new_cart_total += float(item.all_items_cost)
cart.cart_total_cost = new_cart_total
cart.save()
return JsonResponse({
'cart_total': cart.items.count()
})
And here is my models
class CartItem(models.Model):
product = models.ForeignKey(Product, on_delete=models.SET_NULL, null=True)
quantity = models.PositiveIntegerField(null=True, default=1)
item_cost = models.DecimalField(max_digits=9, decimal_places=2, default=0.00)
all_items_cost = models.DecimalField(max_digits=9, decimal_places=2, default=0.00)
def __str__(self):
return str(self.product.title)
class Cart(models.Model):
items = models.ManyToManyField(CartItem, blank=True)
cart_total_cost = models.DecimalField(max_digits=9, decimal_places=2, default=0.00)
def __str__(self):
return str(self.id)
Thanks for any help!
#dirkgroten provided a very good answer, you can also use unique_together option to prevent creating a duplicate entry
suppose, you've three fields name, size, brand in the Product model
and you don't want to create any new entry with the same name, size and brand
You can set it like
class Product:
name = CharField(....)
size = CharField(....)
brand = CharField(....)
field4 = CharField(....)
class Meta:
unique_together = ("name","size","brand")
I personally do not entertain the use of unique_together, but it'll surely prevent from creating multiple entries in these kinds of situations from DB definition side, but you've to handle the same in the code too
The get_or_create function will try to fetch a CartItem with all the exact properties you pass it. In your case you're trying to match against product, item_cost, quantity and all_items_cost. If you pass it the same product with a different quantity, it won't match, it'll create a new CartItem.
Look at the documentation of get_or_create. Use only product for the query and defaults for setting the value when creating a new CartItem:
new_item, created = CartItem.objects.get_or_create(
product=product,
defaults = dict(
item_cost=product.price,
quantity=quantity,
all_items_cost=product.price*quantity),
)

Django FOREIGN KEY constraint failed

My app is a shopping cart. I have a function in views.py that is triggered when someone adds an item to the cart. The function checks if the user has an active order (one that is in the cart but hasn't been paid for).
But the code fails at `user_order, status = Order.objects.get_or_create(owner=user_profile, is_ordered=False)
#login_required()
def add_to_cart(request, **kwargs):
#get the user profile
user_profile = get_object_or_404(UserProfile, user=request.user)
#filter products by id
producto = Producto.objects.filter(id=kwargs.get("pk", "")).first()
#create OrderItem, of the selected product
order_item, status = OrderItem.objects.get_or_create(producto=producto)
if status == False: #adds one to the order_item
order_item.quantity += 1
order_item.save()
print(order_item.quantity)
#create order associated with the user
user_order, status = Order.objects.get_or_create(owner=user_profile, is_ordered=False)
user_order.items.add(order_item)
#print(user_order.items.all()) #queries the items
#print(user_order)
#date is not beign added
# generate a reference code
user_order.ref_code =random.randint(0,100000)
user_order.save()
return HttpResponseRedirect(request.META.get('HTTP_REFERER'))
Relevant models:
class Order(models.Model):
fecha_reparto = models.OneToOneField(DiasDeReparto, on_delete=models.CASCADE, default= 1)
order_nodo = models.OneToOneField(Nodo, on_delete=models.CASCADE, default= 1)
ref_code = models.CharField(max_length=15)
owner = models.ForeignKey(UserProfile, on_delete=models.CASCADE)
is_ordered = models.BooleanField(default=False)
items = models.ManyToManyField(OrderItem)
date_ordered = models.DateTimeField(null=True)
def get_cart_items(self):
return self.items.all()
def get_cart_total(self):
return sum([item.producto.precio * item.quantity for item in self.items.all()])
def __str__(self):
return self.ref_code
class UserProfile(models.Model):
user = models.OneToOneField(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)
class OrderItem(models.Model):
producto = models.OneToOneField(Producto, on_delete=models.SET_NULL, null = True)
quantity = models.IntegerField(default=1)
is_ordered = models.BooleanField(default=False)
date_added = models.DateTimeField(auto_now = True)
date_ordered = models.DateTimeField(null=True)
def __str__(self):
return self.producto.nombre
The problem can be solved with something like this:
try:
Order.objects.get(owner=user_profile)
except:
Order.objects.create(fecha_reparto=dia_reparto[0], order_nodo=nodo[0], ref_code="asdas", owner=user_profile, is_ordered=False, date_ordered=datetime.datetime.now())
But I don't get why get_or_create doesn't work
On your Order model you have a few related model fields and in the get_or_create you are not specifying any defaults. The get or create needs the required values to input if creating and you have a few related fields which are required. That will also explain why the try and except works because you add the fields when creating in the except.
defaults = {fecha_reparto: dia_reparto[0], order_nodo: nodo[0]......}
user_order, status = Order.objects.get_or_create(owner=user_profile, is_ordered=False, defaults=defaults)
Use id instead model instace in this line:
user_order, status = Order.objects.get_or_create(owner=user_profile, is_ordered=False)
So it should look like this:
user_order, status = Order.objects.get_or_create(owner_id=user_profile.pk, is_ordered=False)
It worked for me with similar problem.

Django ORM giving error when trying to use User with additional info

I'm using Django 1.4 with Python 2.7 on Ubuntu 12.04.
Edit: I'm clearing some of the erroneous portions of this question out as it appears the same problem has re-occurred. It was working wonderfully - I made no changes - and now it's failing. What's up with that?
This is basically what the views look like:
#login_required
def request_new_project(request):
"""
.. function:: request_new_project()
Collect information to call the form used to create a new project
:param request: Django Request object
"""
user_dict = { 'rsb_username' : request.user.username}
form = CreateProject(initial = user_dict)
data = { 'user' : request.user }
data.update(csrf(request))
data.update({ 'form' : form })
return render_to_response("create_project.html", data)
#login_required
def add_project(request):
"""
.. function:: add_project()
Add a project for the user
:param request: Django Request object
"""
if (request.method == "POST"):
user = User.objects.get(username = request.POST.get('rsb_username'))
userProfile = UserProfile.objects.get(user = user)
new_project = Projects(client = userProfile,
project_name = request.POST.get('rsb_project_name'),
description = request.POST.get('rsb_description'),
budget = request.POST.get('rsb_budget'),
time_frame = request.POST.get('rsb_time_frame'),
time_frame_units = request.POST.get('rsb_time_frame_units'),
contact = request.POST.get('rsb_point_of_contact'),
contact_email = request.POST.get('rsb_contact_email'),
contact_phone = request.POST.get('rsb_contact_phone'),
price_quote = 0,
eta = 'To Be Determined',
current_status = 'Waiting for quote',
)
new_project.save()
return view_projects(request)
I get the following error:
Cannot assign "<UserProfile: UserProfile object>": "Projects.client" must be a "User" instance.
I didn't change the models.
# Create a table for users
class UserProfile(models.Model):
user = models.OneToOneField(User)
# Client Info
company_name = models.CharField(max_length = 200)
client_type = models.CharField(max_length = 200)
address1 = models.CharField(max_length = 200)
address2 = models.CharField(max_length = 200)
city = models.CharField(max_length = 200)
state = models.CharField(max_length = 200)
country = models.CharField(max_length = 200)
zip_code = models.CharField(max_length = 200)
phone_number = models.CharField(max_length = 200)
# Create a table to manage project requests
class Projects(models.Model):
client = models.ForeignKey(User)
project_name = models.CharField(max_length = 50)
description = models.TextField()
budget = models.CharField(max_length = 50)
time_frame = models.DecimalField(max_digits = 3, decimal_places = 1)
time_frame_units = models.CharField(max_length = 25)
contact = models.CharField(max_length = 50)
contact_email = models.EmailField()
contact_phone = models.CharField(max_length = 25)
price_quote = models.DecimalField(max_digits = 10, decimal_places = 2)
eta = models.CharField(max_length = 200)
current_status = models.CharField(max_length = 200)
Any suggestions?
UPDATE 1:
From the actual database I can see that one of the rsb_projects constraints is:
rsb_projects_client_id_fkey (client_id) REFERENCE rsb_userprofile (id) MATCH SIMPLE ON UPDATE NO ACTION ON DELETE NO ACTION DEFERRABLE INITIALLY DEFERRED
If that helps...
It seems to me that even though I've defined the ForeignKey in the models.py to be against User the database wants the UserProfile id.
Thoughts?
The error is exactly what it says it is.
In order to create the new project you need to give your project object a user profile object, not a user profile id.
user = User.objects.get(id=7)
userprofile = user.get_profile()
project.client = userprofile
Notice that it is userprofile object that is assigned to the project instance's client attribute. You cannot assign the userprofile object's id to the project instance's client attribute.
Also, do not confuse your user id with your userprofile id. They may not be exactly the same.
The user id is the primary key auto-generated in the auth_user table whenever a new user is created in your database.
The userprofile id is the primary key auto-generated in the profiles_userprofile table whenever a corresponding user profile is created that is related to a user.
In other words, your add_project view function needs to read something like
if (request.method == "POST"):
user = User.objects.get(username = request.POST.get('rsb_username'))
# userprofile = UserProfile.objects.get(user = user) <-- we don't need this anymore.
new_project = Projects(client = user, # <--- give it a user instance of course now that your model has changed
project_name = request.POST.get('rsb_project_name'),
description = request.POST.get('rsb_description'),
budget = request.POST.get('rsb_budget'),
time_frame = request.POST.get('rsb_time_frame'),
time_frame_units = request.POST.get('rsb_time_frame_units'),
contact = request.POST.get('rsb_point_of_contact'),
contact_email = request.POST.get('rsb_contact_email'),
contact_phone = request.POST.get('rsb_contact_phone'),
)
new_project.save()
The key takeaway is that you need to be sure what your original model definition is.
If in your Project class, your client attribute is assigned as FK to User, then you need to give it a user object.
If in your Project class, your client attribute is assigned as FK to UserProfile, then you need to give it a userprofile object.

Django duplicate existing record in database

I'm trying to duplicate an existing record in a PostgreSQL DB, it seems to be duplicating by increments of 2 each time I hit the duplicate button. If there's 1 record in the database, once the button is hit it will create records 2 and 3.
Model
class Detail(models.Model):
created = models.DateTimeField(auto_now_add=True, blank=False)
last_update = models.DateTimeField(auto_now=True)
user = models.ForeignKey(User, related_name='+')
draft = models.BooleanField()
outage_name = models.ForeignKey(Outage, related_name='+')
group_name = models.CharField(max_length=100)
shift = models.CharField(max_length=6)
activity = models.CharField(max_length=100, null = False)
culture_title = models.ForeignKey(Culture, related_name='+')
work_completed = models.TextField()
work_planned = models.TextField()
radiation_info = models.TextField()
action_item = models.TextField()
lesson_learned = models.TextField()
View
def turnover_copy(request, id):
obj = Detail.objects.get(pk=id)
obj.pk = None
obj.draft = True
if obj.draft:
user = request.user.id
obj.user_id = user
obj.work_planned = 'My Work Planned.'
obj.save()
return HttpResponse('Created')
else:
return HttpResponse('Unable to duplicate template.')
EDIT: I had the def inside a for loop in the template, so it kept creating duplicates!
Are you sure the code isn't called twice for some reason? Some print statements might help you assert that.