Python / Django: Make a lookup / database query inside a model class - python

What is the right way to lookup a table and use its last value as a value in a new model instance? Something like this:
class MyClass(models.Model):
id = models.AutoField(primary_key=True)
obj = MyClass.objects.latest('id')
my_field = models.IntegerField(default=(obj+1))
I need a db column, which keeps track of the primary_key, but is independent of it and can also be modified. Also I need to use its value as default when creating new instances of the Model.

you can use custom constructor as described in the docs:
https://docs.djangoproject.com/en/2.2/ref/models/instances/
you will need to define the obj field either as integer(to store the id of the previous record) or as a foreign key(if you want to reference the previous db record). In the second case you will need to pass the name of the model to the ForeignKeyField constructor as string('MyClass') and not directly(MyClass).

Related

Django multi-table inheritance - make sure only one child exists (CheckConstraint)

How can I make sure that a parent object has only one child/type?
class Property(...):
class Meta:
abstract = False
class Flat(Property):
pass
class House(Property):
pass
class Land(Property):
pass
I want every property object to have none or at most one child. It can be either flat, house or land (or null).
Is it possible to create a DB constraint for this?
My idea was to create a constraint that checks:
class Meta:
constraints = [
models.CheckConstraint(check=Q(Q(flat__isnull=True) & Q(house__isnull=True))
|
Q(Q(flat__isnull=True) & Q(land__isnull=True))
|
Q(Q(house__isnull=True) & Q(land__isnull=True)),
name="constraint")]
But apparently, there are no such fields on a DB level (you can get flat by property.flat getter in Django but not in DB)
Edit:
properties.Property: (models.E012) 'constraints' refers to the nonexistent field 'flat'.
But apparently, there are no such fields on a DB level (you can get flat by property.flat getter in Django but not in DB)
That is correct: Django adds a property to the Property model to lazily load the related Flat object and will make a query for that, but there is no database field named flat: this is just a query in reverse where Django basically queries with:
SELECT * FROM app_name_flat WHERE property_ptr=pk
with pk the primary key of the property object. It this makes a query.
A CHECK constraint [w3-schools] spans only over a row: it can not look on other rows nor can it look at other tables. It thus can not restrict other tables, and therefore is limited. It can for example prevent one column to have a certain value based on a value for another column in the same row (record), but that is how far a CHECK constraint normally looks.

Read one2many field in inherited model

In odoo v13, the crm.lead model is inherited and modified by the sale_crm module.
In the sale_crm module, the model crm.lead is inherited and a one2many field is added, order_ids. This is an array of sales orders associated with the lead.
I am trying to inherit the crm.lead model, and create a new field that is computed using the order_ids field.
I added sale_crm in the manifest dependencies
I inherit the crm.lead model and attempt to concat the names of all the associated SOs:
class Co7Lead(models.Model):
_inherit = "crm.lead"
so_list = fields.Text(
compute='_get_sos_text',
string="Text list of associated SOs",
help="A comma separated list of SOs associated with this lead")
def _get_sos_text(self):
txt = ""
for order in super(Co7Lead, self).order_ids:
txt += order.name + ""
return txt
Unfortunately, this causes a stack overflow (haha!)
I believe I need to use .browse on the order_ids field but I'm not able to find any examples on how to do this.
The compute method must assign the computed value to the field. If it uses the values of other fields (order_ids.name), it should specify those fields using depends().
You don't need to use super here, self is a record set, so loop over it to access the value of order_ids for each record.
Example:
#api.depends('order_ids.name')
def _get_sos_text(self):
for lead in self:
lead.so_list = "\n".join(order.name for order in lead.order_ids)

Django foreign key new model instance used as default

I'm wondering whether it's possible to do something like the following
class ModelA(models.Model):
my_field = models.ForeignKey('UniqueKey', to_field='key' default=UniqueKey.create(key=KeyGen()))
# KeyGen() returns a new random key
In essence, I have a number of models that have a unique 32 digit key as one of their fields. I have created the UniqueKey model to manage all of those keys and make sure no duplicates ever arise. What I'm trying to do is create a new UniqueKey instance and save it to the database whenever I make a new ModelA instance.
Is this possible or am I just going about this the wrong way?
I think your best approach will be to use the post_save signal when an instance of your model is created. https://docs.djangoproject.com/en/1.9/ref/signals/#post-save
i.e.
class ModelA(models.Model):
my_field = models.ForeignKey('UniqueKey', to_field='key')
def set_default_uniquekey_for_modela(sender, instance, created, raw, using, update_fields):
if created is True:
new_unique_key = UniqueKey(key=KeyGen())
new_unique_key.save()
instance.my_field = new_unique_key
instance.save()
post_save.connect(set_default_uniquekey_for_modela, sender=ModelA)
So when an instance of ModelA is created, it will exectute set_default_uniquekey_for_modela(...) to create a new UniqueKey instance and assign that to the instance.

How to get child model related data in parent model query

I have two models:
class BusinessCard(models.Model):
name = models.CharField(_("name"),null=True,max_length=50)
class Contacts(models.Model):
businesscard_id = models.OneToOneField(BusinessCard,null=True,blank=True,related_name='contact_detail',db_column="businesscard_id")
bcard_json_data = JsonField(null=True)
I just want access contacts model data using business card model:
target_bacard=BusinessCard.objects.filter(id=target_bacard_id).select_related()
When we access the target_bacard.contact_detail it gives key errors.
How can I get the contacts data using target_bacard queryset.
use get() instead of filter() like:
target_bacard = BusinessCard.objects.get(id=target_bacard_id)
target_bacard.contact_detail
If you want to access the Contacts instance that is in the 1-to-1 relationship with a BusinessCard instance bacard, use the related name you specified in Contacts:
contact = bacard.contact_detail
Also, you have some misleading names: Contacts should rather be Contact since an instance of this model represents only one contact. And its field businesscard_id would better be named businesscard (note that the table column will be called businesscard_id at the database level automatically in that case and store the id of the related businesssscard) because in the ORM you get a BusinessCard model instance when you access it, and not just its id.
You have not passed related model (field) argument to select_related()
target_bacard=BusinessCard.objects.filter(id=target_bacard_id).select_related('contact_detail')
Assuming id of BusinessCard is unique, you may want to use ...objects.get(id=target_bacard_id) inplace of ...objects.filter(id=target_bacard_id). Anyway select_related() will work on both ways.
select_related() is used for saving database query.
here is the documentation

How to find out whether a model's column is a foreign key?

I'm dynamically storing information in the database depending on the request:
// table, id and column are provided by the request
table_obj = getattr(models, table)
record = table_obj.objects.get(pk=id)
setattr(record, column, request.POST['value'])
The problem is that request.POST['value'] sometimes contains a foreign record's primary key (i.e. an integer) whereas Django expects the column's value to be an object of type ForeignModel:
Cannot assign "u'122'": "ModelA.b" must be a "ModelB" instance.
Now, is there an elegant way to dynamically check whether b is a column containing foreign keys and what model these keys are linked to? (So that I can load the foreign record by it's primary key and assign it to ModelA?) Or doesn't Django provide information like this to the programmer so I really have to get my hands dirty and use isinstance() on the foreign-key column?
You can use get_field_by_name on the models _meta object:
from django.db.models import ForeignKey
def get_fk_model(model, fieldname):
"""Returns None if not foreignkey, otherswise the relevant model"""
field_object, model, direct, m2m = model._meta.get_field_by_name(fieldname)
if not m2m and direct and isinstance(field_object, ForeignKey):
return field_object.rel.to
return None
Assuming you had a model class MyModel you would use this thus:
fk_model = get_fk_model(MyModel, 'fieldname')
Simple one liner to find all the relations to other models that exist in a model:
In [8]: relations = [f for f in Model._meta.get_fields() if (f.many_to_one or f.one_to_one) and f.auto_created]
Above will give a list of all the models with their relations.
Example:
In [9]: relations
Out[9]:
[<ManyToOneRel: app1.model1>,
<ManyToOneRel: app2.model1>,
<OneToOneRel: app1.model2>,
<OneToOneRel: app3.model5>,
<OneToOneRel: app5.model1>]
I encountered the same use case, and the accepted answer did not work for me directly. I am using Django 1.2 if it's relevant. Instead, I used the get_field_by_name method as follows.
def get_foreign_keys(self):
foreign_keys = []
for field in self._meta.fields:
if isinstance(self._meta.get_field_by_name(field.name)[0], models.ForeignKey):
foreign_keys.append(field.name)
if not foreign_keys:
return None
return foreign_keys
This is a method define inside a class. For my case, what I needed are the names of the ForeignKey fields. Cheers!
Explore the "ModelChoiceField" fields. Can they solve your problem putting foreign keys into forms for you; rather than doing that yourself.
http://docs.djangoproject.com/en/1.1/ref/forms/fields/#fields-which-handle-relationships
record = forms.ModelChoiceField(queryset=table_obj.objects.all())

Categories

Resources