I'm trying to update the value of a OneToOneField from its default value (None), everything seems to work apart the fact that when I call save() the change is not written to the DB.
Instead, if I change the value from the admin console everything works.
My models.py looks like this:
class Payment(m.Model):
[stuff...]
class Order(m.Model):
[stuff...]
active = m.BooleanField(default = False)
payment = m.OneToOneField(Payment, null = True, blank = True, on_delete=m.SET_NULL)
I have a function that activates the order, going like this:
def activate(self, ipn):
[stuff..]
self.payment = Payment(ipn = ipn)
self.payment.save()
self.active = True
self.save()
After i call activate() on an order, the active value gets correctly set to True, but the payment value remains set to None. I can't figure out what is wrong. Any idea?
Ps: as i said, if i manually set the payment from the admin interface everything works.
The payment_id is set when you assign the Payment instance to self.payment. However, the Payment instance does not have an id at that point. You need to save it before you assign it to the OneToOneField.
def activate(self, ipn):
[stuff..]
payment = Payment(ipn = ipn)
payment.save()
self.payment = payment
self.active = True
self.save()
If you try to run your activate code in a shell you will probably see something like this:
ValueError: Cannot assign "...." instance isn't saved in the database.
So basically firstly you need to create a Payment instance:
def activate(self, ipn):
[stuff..]
payment = Payment(ipn = ipn)
payment.save()
self.payment = payment
self.active = True
self.save()
Related
class PurchaseOrder(models.Model):
purchase_order_id = models.AutoField(primary_key=True)
purchase_order_number = models.CharField(unique=True)
vendor = models.ForeignKey(Vendor)
i am creating Purchase Order(po) table. when po created i have to update purchase_order_number as "PO0"+purchase_order_id ex PO0123 (123 is Primary key). so i am using def save in models to accomplish this
def save(self):
if self.purchase_order_id is not None:
self.purchase_order_number = "PO"+str(self.purchase_order_id)
return super(PurchaseOrder, self).save()
It is working fine with single creation but when i try to create bulk of data using locust(Testing tool) its giving an error duplicate entry for PurchseOrdernumber Can we modify field value in models itself some thing like this
purchase_order_number = models.CharField(unique=True,default=("PO"+self.purchase_order_id )
To be honest, I don't think it should work when you create multiple instances. Because as I can see from the code:
if self.purchase_order_id is not None:
self.purchase_order_number = "PO"+str(self.purchase_order_id)
Here purchase_order_id will be None when you are creating new instance. Also, until you call super(PurchaseOrder, self).save(), it will not generate purchase_order_id, meaning purchase_order_number will be empty.
So, what I would recommend is to not store this information in DB. Its basically the same as purchase_order_id with PO in front of it. Instead you can use a property method to get the same value. Like this:
class PurchaseOrder(models.Model):
purchase_order_id = models.AutoField(primary_key=True)
# need to remove `purchase_order_number = models.CharField(unique=True)`
...
#property
def purchase_order_number(self):
return "PO{}".format(self.purchase_order_id)
So, you can also see the purchase_order_number like this:
p = PurchaseOrder.objects.first()
p.purchase_order_number
Downside of this solution is that, you can't make any query on the property field. But I don't think it would be necessary anyway, because you can do the same query for the purchase_order_id, ie PurchaseOrder.objects.filter(purchase_order_id=1).
So, I have a model called ScheduleItem
class ScheduleItem(models.Model):
agreement = FK
location = FK
start = models.DateTimeField()
end = models.DateTimeField()
totalHours = DecimalField
def get_total_hours(self):
start = timedelta(hours=self.start.hour, minutes=self.start.minute)
end = timedelta(hours=self.end.hour, minutes=self.end.minute)
td = (end-start).seconds
totalHours=Decimal(td/Decimal(60)/Decimal(60))
return totalHours
def save(self,*args,**kwargs):
if self.pk == None:
super(ScheduleItem,self).save(self,*args,**kwargs)
self.refresh_from_db() # to access the datetime values, rather than unicode POST
self.totalHours = self.get_total_hours()
else:
self.totalHours = self.get_total_hours()
super(ScheduleItem,self).save(self,*args,**kwargs)
This throws PRIMARY key errors. I get duplicate entries with the second super(ScheduleItem,self). I cannot for the life of me figure out how to check for pk to access the datetime value and then save again within the save override method. I've tried moving things around, I've tried saving within the get_total_hours() function, with nothing but trouble.
I just want the object to be committed to the db so I can get the datetime objects and then calculate the total hours.
I'd rather not convert to datetime within the save function.
Does anyone have any tip or can anyone tell me where I'm going wrong?
You should not pass self to save(). You're calling super().save() as a bound method on an instance, so self is implicitly passed as the first argument. Change it to this:
def save(self,*args,**kwargs):
if self.pk is None:
super(ScheduleItem,self).save(*args,**kwargs)
self.refresh_from_db() # to access the datetime values, rather than unicode POST
self.totalHours = self.get_total_hours()
else:
self.totalHours = self.get_total_hours()
super(ScheduleItem,self).save(*args,**kwargs)
You get this weird behaviour because the first positional argument is force_insert, and the model instance evaluates to True. The second call to super().save() tries to force an insert with the same pk you previously saved.
I have a Model where one boolean ModelField is dependent upon another. The Model is configured like so:
class Situation(models.Model):
ctcs = models.BooleanField(verbose_name="Cross-Technology Critical Situation", blank=True)
has_been_ctcs = models.BooleanField(editable=False, default=False)
The ctcs field is rendered as a checkbox in the ModelForm for this model. What I want to do is, if the ctcs field has been checked, I also want to set has_been_ctcs to True. So what I am doing is setting cleaned_data['has_been_ctcs'] = True on the ModelForm. I've tried doing this both in my view that handles the POST request, as well as within the ModelForm clean function like so:
class SituationForm(forms.ModelForm):
def clean(self):
cleaned_data = super(SituationForm, self).clean()
ctcs = cleaned_data.get("ctcs")
if ctcs:
self.cleaned_data['has_been_ctcs'] = True
return cleaned_data
And here is the snippet from the view that handles creation of a new Situation model:
sit_form = SituationForm(request.POST)
if sit_form.is_valid():
print sit_form.cleaned_data['ctcs'] # Prints True
if sit_form.cleaned_data['ctcs']:
print "Checking form has_been_ctcs"
# Have also tried setting sit_form.cleaned_data['has_been_ctcs'] here, no difference from doing it in `def clean()`
print sit_form.cleaned_data['has_been_ctcs'] # Prints True
sit = sit_form.save()
print sit.has_been_ctcs # Prints False
I cannot seem to get the has_been_ctcs value of True to propagate to the Situation model. How can I go about doing this?
cleaned_data only works for fields that are included in the form. What you want to do is:
sit_form = SituationForm(request.POST)
if sit_form.is_valid():
if sit_form.cleand_data['ctcs']:
sit_form.instance.has_been_ctcs = True
sit = sit_form.save()
I have this manager in my models.py
class ItemManager(models.Manager):
def get_fee(self):
from django.db import connection
cursor = connection.cursor()
cursor.execute("""
SELECT fee
FROM item
WHERE itemID = %d AND item.type = %d
""", [self.myItemID, self.myItemType])
fee = cursor.fetchone()
return fee
and class
Sample(models.Model):
sampleID = models.AutoField(primary_key=True)
itemID = models.ForeignKey(Item)
item.type = models.ForeignKey(Item)
...
def save(self, *args, **kwargs):
is_new = self.pk is None
super(Sample, self).save(*args, **kwargs)
if is_new:
cd.amount = MonthlyFeeManager()
cd.save()
Then it produces an error:
Cannot convert <myapp.models.ItemManager object at 0xa6f07ec> to Decimal
In general, i want to execute a RAW SQL query in a manager and use it to get the result from the query. I tried to search but most returns are tuples, not a value.
This is not how you use a manager. Even with a perfectly normal class instance, your attempt wouldn't give you what you wanted: you would need to instantiate it and call get_fee() on the instance.
With a manager, you don't need to instantiate it, because that's already done for you as the objects attribute on the model. But you still need to call the relevant method:
cd.amount = Sample.objects.get_fee()
However, this still won't work. That's because you've referred to self.myItemId and self.myItemType, which don't exist on the Manager object. Which leads me to the conclusion that you don't want a Manager object at all: you just want a standard model method. And there's no need for the raw SQL, either: your code is perfectly easily expressed using normal model query syntax.
(I can't show you exactly what it would look like, because the ForeignKeys in your example don't make any sense, and it's not clear where fee is supposed to be coming from.)
I'm trying to write an internal API in my application without necessarily coupling it with the database.
class Product(models.Model):
name=models.CharField(max_length=4000)
price=models.IntegerField(default=-1)
currency=models.CharField(max_length=3, default='INR')
class Image(models.Model):
# NOTE -- Have changed the table name to products_images
width=models.IntegerField(default=-1)
height=models.IntegerField(default=-1)
url=models.URLField(max_length=1000, verify_exists=False)
product=models.ForeignKey(Product)
def create_product:
p=Product()
i=Image(height=100, widght=100, url='http://something/something')
p.image_set.add(i)
return p
Now, when I call create_product() Django throws up an error:
IntegrityError: products_images.product_id may not be NULL
However, if I call p.save() & i.save() before calling p.image_set.add(i) it works. Is there any way that I can add objects to a related object set without saving both to the DB first?
def create_product():
product_obj = Product.objects.create(name='Foobar')
image_obj = Image.objects.create(height=100, widght=100, url='http://something/something', product=product_obj)
return product_obj
Explanation:
Product object has to be created first and then assign it to the Image object because id and name here is required field.
I am wondering why wouldn't you not require to make a product entry in DB in first case? If there is any specific reason then i may suggest you some work around?
EDIT: Okay! i think i got you, you don't want to assign a product to an image object initially. How about creating a product field as null is equal to true.
product = models.ForeignKey(Product, null=True)
Now, your function becomes something like this:
def create_product():
image_obj = Image.objects.create(height=100, widght=100, url='http://something/something')
return image_obj
Hope it helps you?
I got same issue with #Saurabh Nanda
I am using Django 1.4.2. When I read in django, i see that
# file django/db/models/fields/related.py
def get_query_set(self):
try:
return self.instance._prefetched_objects_cache[rel_field.related_query_name()]
except (AttributeError, KeyError):
db = self._db or router.db_for_read(self.model, instance=self.instance)
return super(RelatedManager,self).get_query_set().using(db).filter(**self.core_filters)
# file django/db/models/query.py
qs = getattr(obj, attname).all()
qs._result_cache = vals
# We don't want the individual qs doing prefetch_related now, since we
# have merged this into the current work.
qs._prefetch_done = True
obj._prefetched_objects_cache[cache_name] = qs
That 's make sese, we only need to set property _prefetched_objects_cache for the object.
p = Product()
image_cached = []
for i in xrange(100):
image=Image(height=100, widght=100, url='http://something/something')
image_cached.append(image)
qs = p.images.all()
qs._result_cache = image_cached
qs._prefetch_done = True
p._prefetched_objects_cache = {'images': qs}
Your problem is that the id isn't set by django, but by the database (it's represented in the database by an auto-incremented field), so until it's saved there's no id. More about this in the documentation.
I can think of three possible solutions:
Set a different field of your Image model as the primary key (documented here).
Set a different field of your Production model as the foreign key (documented here).
Use django's database transactions API (documented here).