How to update object with another object in get_or_create? - python

I have to tables wit similar fields and I want to copy objects from one table to another.
Problem that object could be absent in second table, so I have to use get_or_create() method:
#these are new products, they all are instances of NewProduct model, which is similar
#to Product model
new_products_list = [<NewProduct: EEEF0AP>, <NewProduct: XR3D-F>,<Product: XXID-F>]
#loop over them and check if they are already in database
for product in new_products_list:
product, created = Products.objects.get_or_create(article=product.article)
if created:
#here is no problem because new object saved
pass
else:
# here I need to code that will update existing Product instance
# with values from NewProduct instance fields
The case is that I don't want to list all fields for update manually, like this,, because I have about 30 of them:
update_old_product = Product(name=new_product.name,article= new_product.article)
Please advise more elegant way than above

You can loop over the field names and update them in the the other Product instance:
for new_product in new_products_list:
# use different variable names, otherwise you won't be able to access
# the item from new_product_list here
product, created = Products.objects.get_or_create(article=new_product.article)
if not created:
for field in new_product._meta.get_all_field_names():
setattr(product, field, getattr(new_product, field))
product.save()

You could try something like this.
def copy_fields(frm, to):
id = to.id
for field in frm.__class__._meta.fields:
setattr(to, field.verbose_name, field._get_val_from_obj(frm))
to.id = id

This is similar to Ashwini Chaudhary, although I think it will take care of that error that you mentioned in the comments.
new_products_list= (
# obj1, obj2, obj3 would be from [<NewProduct: EEEF0AP>, <NewProduct: XR3D-F>,<Product: XXID-F>] in your question
# NewProduct would just be the model that you import
# NewProduct._meta.fields would be all the fields
(obj1, NewProduct, NewProduct._meta.fields,),
(obj2, NewProduct, NewProduct._meta.fields,),
(obj3, NewProduct, NewProduct._meta.fields,),
)
for instance, model, fields in new_products_list:
new_fields = {}
obj, created = model.objects.get_or_create(pk=instance.article) # this is pretty much just here to ensure that it is created for filter later
for field in fields:
if field != model._meta.pk: # do not want to update the pk
new_fields[field.name] = request.POST[field.name]
model.objects.filter(pk=question_id).update(**new_fields) # you won't have to worry about updating multiple models in the db because there can only be one instance with this pk
I know this was over a month ago, but I figured I would share my solution even if you have already figured it out

Related

how to update old records with computed field with store = True

I want to update the "on_hand_qty" field of existing record
_inherit="product.product"
on_hand_qty=fields.Float('onhand', compute='copy_quant' ,store=True)
#api.constrains('qty_available')
def copy_quant(self):
for rec in self:
rec.on_hand_qty = rec.qty_available
I want this field with store =True
but this field is not updated in old records with store= True. please suggest how to achieve this.
user10810227
Make the related field of qty_available
on_hand_qty=fields.Float(related='qty_available', string="Your Field")

How to prevent field value repetition on already assigned date in Odoo?

I am working with Odoo 10.
I have a one2many field with two columns in the hr.employee model. If the field "Bonus" (many2one field) is assigned to a particular date, it should not be saved or repeated once again on the same date.
How to achieve this?
Take a look at this below code, this is one possible solution, not the best.
from odoo import models, fields, api
from odoo.exceptions import ValidationError
class HrEmployee(models.Model):
_inherit = 'hr.employee'
prod_details_ids = fields.One2many(
string=u'Product details',
comodel_name='prod.details',
inverse_name='employee_id',
)
class ProdDetails(models.Model):
_name = 'prod.details'
employee_id = fields.Many2one(
string=u'Employee',
comodel_name='hr.employee',
)
date = fields.Date(
string=u'Date',
default=fields.Date.context_today,
)
bonus_id = fields.Many2one(
string=u'Bonus',
comodel_name='res.partner', # just an example
)
And then you need to add the constrains:
Solution 1
_sql_constraints = [
('bonus_unique', 'unique(employee_id, date, bonus_id)',
_('Date + Bonus cannot be repeated in one employee!')),
]
Solution 2
#api.one
#api.constrains('date', 'bonus_id')
def _check_unique_date(self):
# you have more freedom here if you want to check more things
rest = self.employee_id.prod_details_ids - self
for record in rest:
if record.date == self.date and record.bonus_id.id == self.bonus_id.id:
raise ValidationError("Date + Bonus already exists and violates unique field constraint")
Note: If you have date already in your database make sure that the constrains can be added with this data, because if not the constraint cannot be added to the database. This happens with the _sql_constraints at least
Use constrains to stop creating another record with the same name, so duplication of records doesnot occur.
you can use constraints and the search_count() method to check if there is a record. like below
#api.constraints('date')
def validate_date(self):
result = self.search_count([your_domain])
if result:
raise ValidationError(_('Your Text'))

Can I lookup a related field using a Q object in the Django ORM?

In Django, can I re-use an existing Q object on multiple models, without writing the same filters twice?
I was thinking about something along the lines of the pseudo-Django code below, but did not find anything relevant in the documentation :
class Author(Model):
name = TextField()
company_name = TextField()
class Book(Model):
author = ForeignKey(Author)
# Create a Q object for the Author model
q_author = Q(company_name="Books & co.")
# Use it to retrieve Book objects
qs = Book.objects.filter(author__matches=q_author)
If that is not possible, can I extend an existing Q object to work on a related field? Pseudo-example :
# q_book == Q(author__company_name="Books & co.")
q_book = q_author.extend("author")
# Use it to retrieve Book objects
qs = Book.objects.filter(q_book)
The only thing I've found that comes close is using a subquery, which is a bit unwieldy :
qs = Book.objects.filter(author__in=Author.objects.filter(q_author))
From what I can tell by your comment, it just looks like you're trying to pass a set of common arguments to multiple filters, to do that you can just unpack a dictionary
The values in the dictionary can still be q objects if required as if it were a value you would pass in to the filter argument normally
args = { 'author__company_name': "Books & co" }
qs = Book.objects.filter(**args)
args['author_name'] = 'Foo'
qs = Book.objects.filter(**args)
To share this between different models, you'd have to do some dictionary mangling
author_args = { k.lstrip('author__'): v for k, v in args.items }
You can do this
books = Book.objects.filter(author__company_name="Books & co")

django-tables2 add dynamic columns to table class from hstore

My general question is: can I use the data stored in a HStoreField (Django 1.8.9) to generate columns dynamically for an existing Table class of django-tables2? As an example below, say I have a model:
from django.contrib.postgres import fields as pgfields
GameSession(models.Model):
user = models.ForeignKey('profile.GamerProfile')
game = models.ForeignKey('games.Game')
last_achievement = models.ForeignKey('games.Achievement')
extra_info = pgfields.HStoreField(null=True, blank=True)
Now, say I have a table defined as:
GameSessionTable(tables.Table):
class Meta(BaseMetaTable):
model = GameSession
fields = []
orderable=False
id = tables.LinkColumn(accessor='id', verbose_name='Id', viewname='reporting:session_stats', args=[A('id')], attrs={'a':{'target':'_blank'}})
started = DateTimeColumn(accessor='startdata.when_started', verbose_name='Started')
stopped = DateTimeColumn(accessor='stopdata.when_stopped', verbose_name='Stopped')
game_name = tables.LinkColumn(accessor='game.name', verbose_name='Game name', viewname='reporting:game_stats', args=[A('mainjob.id')], attrs={'a':{'target':'_blank'}})
I want to be able to add columns for each of the keys stored in the extra_info column for all of the GameSessions. I have tried to override the init() method of the GameSessionTable class, where I have access to the queryset, then make a set of all the keys of my GameSession objects, then add them to self, however that doesn't seem to work. Code below:
def __init__(self, data, *args, **kwargs):
super(GameSessionTable, self).__init__(data, *args, **kwargs)
if data:
extra_cols=[]
# just to be sure, check that the model has the extra_info HStore field
if data.model._meta.get_field('extra_info'):
extra_cols = list(set([item for q in data if q.extra_info for item in q.extra_info.keys()]))
for col in extra_cols:
self.columns.columns[col] = tables.Column(accessor='extra_info.%s' %col, verbose_name=col.replace("_", " ").title())
Just a mention, I have had a look at https://spapas.github.io/2015/10/05/django-dynamic-tables-similar-models/#introduction but it's not been much help because the use case there is related to the fields of a model, whereas my situation is slightly different as you can see above.
Just wanted to check, is this even possible or do I have to define an entirely different table for this data, or potentially use an entirely different library altogether like django-reports-builder?
Managed to figure this out to a certain extent. The code I was running above was slightly wrong, so I updated it to run my code before the superclass init() gets run, and changed where I was adding the columns.
As a result, my init() function now looks like this:
def __init__(self, data, *args, **kwargs):
if data:
extra_cols=[]
# just to be sure, check that the model has the extra_info HStore field
if data.model._meta.get_field('extra_info'):
extra_cols = list(set([item for q in data if q.extra_info for item in q.extra_info.keys()]))
for col in extra_cols:
self.base_columns[col] = tables.Column(accessor='extra_info.%s' %col, verbose_name=col.replace("_", " ").title())
super(GameSessionTable, self).__init__(data, *args, **kwargs)
Note that I replaced self.columns.columns (which were BoundColumn instances) with self.base_columns. This allows the superclass to then consider these as well when initializing the Table class.
Might not be the most elegant solution, but it seems to do the trick for me.

Django Model() vs Model.objects.create()

What it the difference between running two commands:
foo = FooModel()
and
bar = BarModel.objects.create()
Does the second one immediately create a BarModel in the database, while for FooModel, the save() method has to be called explicitly to add it to the database?
https://docs.djangoproject.com/en/stable/topics/db/queries/#creating-objects
To create and save an object in a single step, use the create() method.
The differences between Model() and Model.objects.create() are the following:
INSERT vs UPDATE
Model.save() does either INSERT or UPDATE of an object in a DB, while Model.objects.create() does only INSERT.
Model.save() does
UPDATE If the object’s primary key attribute is set to a value that evaluates to True
INSERT If the object’s primary key attribute is not set or if the UPDATE didn’t update anything (e.g. if primary key is set to a value that doesn’t exist in the database).
Existing primary key
If primary key attribute is set to a value and such primary key already exists, then Model.save() performs UPDATE, but Model.objects.create() raises IntegrityError.
Consider the following models.py:
class Subject(models.Model):
subject_id = models.PositiveIntegerField(primary_key=True, db_column='subject_id')
name = models.CharField(max_length=255)
max_marks = models.PositiveIntegerField()
Insert/Update to db with Model.save()
physics = Subject(subject_id=1, name='Physics', max_marks=100)
physics.save()
math = Subject(subject_id=1, name='Math', max_marks=50) # Case of update
math.save()
Result:
Subject.objects.all().values()
<QuerySet [{'subject_id': 1, 'name': 'Math', 'max_marks': 50}]>
Insert to db with Model.objects.create()
Subject.objects.create(subject_id=1, name='Chemistry', max_marks=100)
IntegrityError: UNIQUE constraint failed: m****t.subject_id
Explanation: In the example, math.save() does an UPDATE (changes name from Physics to Math, and max_marks from 100 to 50), because subject_id is a primary key and subject_id=1 already exists in the DB. But Subject.objects.create() raises IntegrityError, because, again the primary key subject_id with the value 1 already exists.
Forced insert
Model.save() can be made to behave as Model.objects.create() by using force_insert=True parameter: Model.save(force_insert=True).
Return value
Model.save() return None where Model.objects.create() return model instance i.e. package_name.models.Model
Conclusion: Model.objects.create() does model initialization and performs save() with force_insert=True.
Excerpt from the source code of Model.objects.create()
def create(self, **kwargs):
"""
Create a new object with the given kwargs, saving it to the database
and returning the created object.
"""
obj = self.model(**kwargs)
self._for_write = True
obj.save(force_insert=True, using=self.db)
return obj
For more details follow the links:
https://docs.djangoproject.com/en/stable/ref/models/querysets/#create
https://github.com/django/django/blob/2d8dcba03aae200aaa103ec1e69f0a0038ec2f85/django/db/models/query.py#L440
The two syntaxes are not equivalent and it can lead to unexpected errors.
Here is a simple example showing the differences.
If you have a model:
from django.db import models
class Test(models.Model):
added = models.DateTimeField(auto_now_add=True)
And you create a first object:
foo = Test.objects.create(pk=1)
Then you try to create an object with the same primary key:
foo_duplicate = Test.objects.create(pk=1)
# returns the error:
# django.db.utils.IntegrityError: (1062, "Duplicate entry '1' for key 'PRIMARY'")
foo_duplicate = Test(pk=1).save()
# returns the error:
# django.db.utils.IntegrityError: (1048, "Column 'added' cannot be null")
UPDATE 15.3.2017:
I have opened a Django-issue on this and it seems to be preliminary accepted here:
https://code.djangoproject.com/ticket/27825
My experience is that when using the Constructor (ORM) class by references with Django 1.10.5 there might be some inconsistencies in the data (i.e. the attributes of the created object may get the type of the input data instead of the casted type of the ORM object property)
example:
models
class Payment(models.Model):
amount_cash = models.DecimalField()
some_test.py - object.create
Class SomeTestCase:
def generate_orm_obj(self, _constructor, base_data=None, modifiers=None):
objs = []
if not base_data:
base_data = {'amount_case': 123.00}
for modifier in modifiers:
actual_data = deepcopy(base_data)
actual_data.update(modifier)
# Hacky fix,
_obj = _constructor.objects.create(**actual_data)
print(type(_obj.amount_cash)) # Decimal
assert created
objs.append(_obj)
return objs
some_test.py - Constructor()
Class SomeTestCase:
def generate_orm_obj(self, _constructor, base_data=None, modifiers=None):
objs = []
if not base_data:
base_data = {'amount_case': 123.00}
for modifier in modifiers:
actual_data = deepcopy(base_data)
actual_data.update(modifier)
# Hacky fix,
_obj = _constructor(**actual_data)
print(type(_obj.amount_cash)) # Float
assert created
objs.append(_obj)
return objs
Model.objects.create() creates a model instance and saves it. Model() only creates an in memory model instance. It's not saved to the database until you call the instance's save() method to save it. That's when validation happens also.

Categories