I have one model (company category) populated by a table - simple names etc. I then have a company model and I'd like to link these two together such that I have have categories in a populated drop down box.
class CompanyCategory(db.Model):
categoryname = db.StringProperty(required=True)
class Company(db.Model):
companyurl = db.StringProperty(required=False)
companycategory = db.ReferenceProperty(CompanyCategory, collection_name='category')
However when I do this I get the following error:
<class 'google.appengine.ext.db.DuplicatePropertyError'>: Class CompanyCategory already has property categoryname
Traceback (most recent call last):
File "/base/data/home/apps/XXX/1.358759458095086806/showcompanies.py", line 52, in <module>
class Company(db.Model):
File "/base/python_runtime/python_lib/versions/1/google/appengine/ext/db/__init__.py", line 514, in __init__
_initialize_properties(cls, name, bases, dct)
File "/base/python_runtime/python_lib/versions/1/google/appengine/ext/db/__init__.py", line 429, in _initialize_properties
attr.__property_config__(model_class, attr_name)
File "/base/python_runtime/python_lib/versions/1/google/appengine/ext/db/__init__.py", line 3656, in __property_config__
self.collection_name))
How do I make this work? I have looked at some examples, but I'm not able to make work in this case. How can I make the companycategory a list.
Here is what is in the table:
CompanyCategory Entities
‹ Prev 20 1-1 Next 20 ›
ID/Name categoryname
id=96001 ss
This is where the model definition is the other way around. I want each company to have have a category which is populated from a table. With the following definition I can get something that resembles what I am looking for:
class CompanyCategory(db.Model):
categoryname = db.StringProperty(required=False)
def __unicode__(self):
return u'%s' % (self.categoryname)
class Company(db.Model):
companyurl = db.StringProperty(required=False)
companyname = db.StringProperty(required=False)
companydesc = db.TextProperty(required=False)
companyaddress = db.PostalAddressProperty(required=False)
companypostcode = db.StringProperty(required=False)
companyemail = db.EmailProperty(required=False)
companycountry = db.StringProperty(required=False)
companyvalid = db.BooleanProperty()
companyentrytime = db.DateTimeProperty(auto_now_add=True)
companylatlong = db.GeoPtProperty()
companycategory = db.ReferenceProperty(CompanyCategory)
I now get a drop down box when I go to add a company - populated from the values in the table. However I'm not getting the value's in the drop down populated into the text value in the datastore upon submission. How should this be done?
Pasting your code into shell.appspot.com works just fine - so whatever the cause of your problem, your sample code is not representative of it. As Christopher Ramirez suggests, you're probably declaring companycategory twice, either in the same model, or in a parent class of the model.
Related
I want to get all of objects that are related to an instance of models.
Because my code is kinda generic, I pass the related table as an string and use eval() function to convert it to the related table class. But I got an error.
Suppose that we have an instance of a table like self.casefile; this is a part of my code:
def related_models_migration(self):
opts = self.casefile._meta
table_name = 'Files'
for f in opts.many_to_many:
name = ''.join(f.name.split('_'))
table_name += name.capitalize()
objects = self.casefile.eval(table_name).all()
and I got this error:
AttributeError Traceback (most recent call last)
<ipython-input-6-025484eeba97> in <module>
----> 1 obj.related_models_migration()
~/Documents/kangaroo/etl/data_migration.py in related_models_migration(self)
28 name = ''.join(f.name.split('_'))
29 table_name += name.capitalize()
---> 30 objects = self.casefile.eval(table_name).all()
31
32 for d in dir(etl.models):
AttributeError: 'FilesCasefiles' object has no attribute 'eval'
How can I pass the class name?
You can not use eval(..) for that. What you probably want to use here is getattr(..):
def related_models_migration(self):
opts = self.casefile._meta
table_name = 'Files'
for f in opts.many_to_many:
name = ''.join(f.name.split('_'))
table_name += name.capitalize()
objects = getattr(self.casefile, table_name).all()
I am not sure you should use table_name += … here however, since it will each time add more content to the table_name. You likely want to use something like table_name = 'Files{}'.format(name.capitalize()).
Note: normally related fields are not capitalized. One writes users or user_set, not Users.
Django provides a way to do this, although you do need to specify the name of the app in which the moodel is defined (because it's possible to have two models with the same name in different apps).
apps.get_model(app_label, model_name, require_ready=True)¶
Returns the Model with the given app_label and model_name.
As a shortcut, this method also accepts a single argument in the form
app_label.model_name. model_name is case-insensitive.
This code only creates one record. What is wrong?
class PartnerTagCreate(models.TransientModel):
""" Choose tags to be added to partner."""
_name = 'partner.tags.create'
_description = __doc__
market_id = fields.Many2one('partner.tags', string='Market Tag')
application_id = fields.Many2one('partner.tags', string='Application Tag')
partner_id = fields.Integer()
#api.multi
def create_contact_tag(self):
for record in self.env['sale.order.line'].browse(self._context.get('active_ids', [])):
vals = {}
vals['partner_id'] = record.order_partner_id
self.write(vals)
return True
I need this function to create one record for each order_partner_id I selected before opening the wizard...
How to achieve that?
Here my new code (function) ...
def create_contact_tag(self):
sale_order_line_ids = self.env['sale.order.line'].browse(self._context.get('active_ids', []))
for partner in sale_order_line_ids:
values = {}
values['partner_id'] = partner.order_partner_id
self.create(values)
return {}
This creates one record for marketing_id and/or application_id and dedicated records for each partner_id in the record.
You use the 'create' method to create new records; this is the same for TransientModel as for the persistent Model.
So, replace
self.write(vals)
by
self.create(vals)
and you should be fine.
I want to write a model method that modifies it's nested fields
I'm having trouble iterating through an object that is related to the main model. The code in particular is:
def set_si_units(self):
self.currently.get_si_units()
for i in range(0, self.hourly.data.count()):
self.hourly.data[i].get_si_units()
The 2nd line that modifies self.currently runs without a hitch and I receive converted temperatures. The for loop however gives me the following error:
TypeError: 'RelatedManager' object does not support indexing
I'd really like to be able to iterate through each instance of the Data model individually so I can convert the temperatures as I am doing with the Currently model.
I've included the relevant code below as well. Please let me know if you need to see something else. Any help or feedback with regards to my approach is greatly appreciated!
Traceback
File "/path_to_project/project/weather/models.py", line 137, in get_si_units
self.hourly.data[i] = self.hourly.data[i].get_si_units()
TypeError: 'RelatedManager' object does not support indexing
Classes with get_si_units() (eg. Currently & Data)
class SomeClass(model.Models):
temperature = models.FloatField(null=True, blank=True)
... # Other fields
def convert_f_to_c(self, temperature_f):
...
def get_si_units(self):
data_point = self
data_point.temperature = self.convert_f_to_c(self.temperature)
... # Convert other fields
return data_point
Location class that I'm stuck on
class Location(models.Model):
currently = models.OneToOneField(Currently, on_delete=models.CASCADE)
hourly = models.OneToOneField(Hourly, on_delete=models.CASCADE)
...
def set_si_units(self):
self.currently.get_si_units()
for i in range(0, self.hourly.data.count()):
self.hourly.data[i].get_si_units()
You can use get all objects then iterate over it.
class Location(models.Model):
currently = models.OneToOneField(Currently, on_delete=models.CASCADE)
hourly = models.OneToOneField(Hourly, on_delete=models.CASCADE)
...
def set_si_units(self):
self.currently.get_si_units()
for hourly_data in self.hourly.data.all():
hourly_data.get_si_units()
I'm using python-storm as orm. The many-to-many reference set is giving me headaches :(
These are the relevant objects:
class Author(object):
__storm_table__ = "author"
id = Int(primary=True)
name = Unicode()
institution_id = Int()
institution = Reference(institution_id, Institution.id)
def __init__(self, name):
self.name = name
class Paper(object):
__storm_table__ = "paper"
id = Int(primary=True)
name = Unicode()
conference_id = Int()
conference = Reference(conference_id, Conference.id)
def __init__(self, name):
self.name = name
class AuthorPapers(object):
__storm_table__ = "authorpapers"
__storm_primary__ = "author_id", "paper_id"
author_id = Int()
paper_id = Int()
The respective sqlite table look like this
store.execute("CREATE TABLE if not exists author (id INTEGER PRIMARY KEY, name VARCHAR, institution_id INTEGER, FOREIGN KEY (institution_id) REFERENCES institution(id))")
store.execute("CREATE TABLE if not exists paper (id INTEGER PRIMARY KEY, name VARCHAR, conference_id INTEGER, FOREIGN KEY (conference_id) REFERENCES conference(id))")
store.execute("CREATE TABLE if not exists authorpapers (author_id INTEGER, paper_id INTEGER, PRIMARY KEY (author_id, paper_id))")
Now say if a have two author the collaborated on a paper
a = Author(u"Steve Rogers")
b = Author(u"Captain America")
and a paper
p6 = Paper(u"Bunga Bunga")
So now I want to associate both author to the paper using
Author.papers = ReferenceSet(Author.id, AuthorPapers.author_id, Paper.id, AuthorPapers.paper_id)
and doing this
a.papers.add(p6)
b.papers.add(p6)
This is btw it says it is supposed to work in the storm tutorial...but I get
File "/usr/lib64/python2.7/site-packages/storm/references.py", line 376, in add
self._relation2.link(remote, link, True)
File "/usr/lib64/python2.7/site-packages/storm/references.py", line 624, in link
pairs = zip(self._get_local_columns(local.__class__),
File "/usr/lib64/python2.7/site-packages/storm/references.py", line 870, in _get_local_columns
for prop in self.local_key)
File "/usr/lib64/python2.7/site-packages/storm/references.py", line 870, in <genexpr>
for prop in self.local_key)
File "/usr/lib64/python2.7/site-packages/storm/properties.py", line 53, in __get__
return self._get_column(cls)
File "/usr/lib64/python2.7/site-packages/storm/properties.py", line 97, in _get_column
attr = self._detect_attr_name(cls)
File "/usr/lib64/python2.7/site-packages/storm/properties.py", line 82, in _detect_attr_name
raise RuntimeError("Property used in an unknown class")
RuntimeError: Property used in an unknown class
And I'm not really able to make sense of this right now.
I'm not really, familiar with storm, but looking at the documentation example, looks like is just an issue related to the order in which the arguments to ReferenceSet are passed. I tried to use this:
Author.papers = ReferenceSet(Author.id, AuthorPapers.author_id, AuthorPapers.paper_id, Paper.id)
instead of this:
Author.papers = ReferenceSet(Author.id, AuthorPapers.author_id, Paper.id, AuthorPapers.paper_id)
and no exception was raised.
I have some problems with setting up the dictionary collection in Python's SQLAlchemy:
I am using declarative definition of tables. I have Item table in 1:N relation with Record table. I set up the relation using the following code:
_Base = declarative_base()
class Record(_Base):
__tablename__ = 'records'
item_id = Column(String(M_ITEM_ID), ForeignKey('items.id'))
id = Column(String(M_RECORD_ID), primary_key=True)
uri = Column(String(M_RECORD_URI))
name = Column(String(M_RECORD_NAME))
class Item(_Base):
__tablename__ = 'items'
id = Column(String(M_ITEM_ID), primary_key=True)
records = relation(Record, collection_class=column_mapped_collection(Record.name), backref='item')
Now I want to work with the Items and Records. Let's create some objects:
i1 = Item(id='id1')
r = Record(id='mujrecord')
And now I want to associate these objects using the following code:
i1.records['source_wav'] = r
but the Record r doesn't have set the name attribute (the foreign key). Is there any solution how to automatically ensure this? (I know that setting the foreign key during the Record creation works, but it doesn't sound good for me).
Many thanks
You want something like this:
from sqlalchemy.orm import validates
class Item(_Base):
[...]
#validates('records')
def validate_record(self, key, record):
assert record.name is not None, "Record fails validation, must have a name"
return record
With this, you get the desired validation:
>>> i1 = Item(id='id1')
>>> r = Record(id='mujrecord')
>>> i1.records['source_wav'] = r
Traceback (most recent call last):
[...]
AssertionError: Record fails validation, must have a name
>>> r.name = 'foo'
>>> i1.records['source_wav'] = r
>>>
I can't comment yet, so I'm just going to write this as a separate answer:
from sqlalchemy.orm import validates
class Item(_Base):
[...]
#validates('records')
def validate_record(self, key, record):
record.name=key
return record
This is basically a copy of Gunnlaugur's answer but abusing the validates decorator to do something more useful than exploding.
You have:
backref='item'
Is this a typo for
backref='name'
?