4

I have 2 model classes as below:

class Domain(db.Model):
    __tablename__ = 'domain'
    id = db.Column(db.Integer, primary_key=True)
    domain_name = db.Column(db.String(30), unique=True)
    mailboxes = db.Column(db.Integer, default=0)

    def __init__(self, **kwargs):
        self.__dict__.update(kwargs)

    def __repr__(self):
        return '%s'  % self.domain_name


class EmailAccount(db.Model):
    __tablename__ = 'email_account'
    __table_args__ = (
        db.UniqueConstraint('username', 'domain_id', 
                            name='_uq_username_domain'),{}
    )
    id = db.Column(db.Integer, primary_key=True)
    username = db.Column(db.String(30))
    domain_id = db.Column(db.Integer, db.ForeignKey('domain.id'))
    domain = db.relationship('Domain', backref=db.backref('emailaccounts',
                            lazy='dynamic'))
    def __init__(self,**kwargs):
        self.__dict__.update(kwargs)

    def __repr__(self):
         return  '%s@%s ' % (self.username, self.domain)

I have added only the relevant attributes required here in the example. I wish to populate the model using a script by reading a csv file for the data. The script for domain table works well using Flask-SQLAlchemy, but the script for emailaccount table throws out exception. The script is as follows:

#Populate domains from csv
domain_file = "domain.csv"
csv_file = csv.DictReader(open(domain_file, 'rb'), delimiter=',')
for row in csv_file:
    #data type conversion from (csv)string before inserting to table
    for key, value in row.items():
          #some code omitted
        print key, value    
    domain = Domain(**row)
    db.session.add(domain)
    db.session.commit()

#Populate accounts from csv
accounts_file = "accounts.csv"
csv_file = csv.DictReader(open(accounts_file, 'rb'), delimiter=',')

for row in csv_file:
    mdomain_name = ''
    #data type conversion from (csv)string before inserting to table
    for key, value in row.items():
        print key, value
        if key == 'domain':
            mdomain = Domain.query.filter_by(domain_name = value).first()
            mdomain_name = mdomain.domain_name
            mdomain_id = mdomain.id
        if key == 'domain_id':
            value = mdomain_id
    account = EmailAccount(**row)
    db.session.add(account)
    db.session.commit()

Exception thrown is:

File "data.py", line 55, in db.session.add(account)
File ".../local/lib/python2.7/site-packages/sqlalchemy/orm/scoping.py", line 149, in do return getattr(self.registry(), name)(*args, **kwargs)
File ".../local/lib/python2.7/site-packages/sqlalchemy/orm/session.py", line 1397, in add self._save_or_update_state(state)
File ".../local/lib/python2.7/site-packages/sqlalchemy/orm/session.py", line 1415, in _save_or_update_state halt_on=self._contains_state):
File ".../local/lib/python2.7/site-packages/sqlalchemy/orm/mapper.py", line 1986, in cascade_iterator parent_dict, visited_states, halt_on))
File ".../local/lib/python2.7/site-packages/sqlalchemy/orm/properties.py", line 930, in cascade_iterator get_all_pending(state, dict_)
File ".../local/lib/python2.7/site-packages/sqlalchemy/orm/attributes.py", line 761, in get_all_pending ret = [(instance_state(current), current)] AttributeError: 'str' object has no attribute '_sa_instance_state'

Pl. revert with the changes in the code in the script for data.py i.e script for uploading data for EmailAccount model which has foreignkey of Domain class. I wish to use Flask-SQLAlchemy only.

Extract of accounts.csv file:

Email Account,legacy_username,password,full_name,quota,is_domain_admin,is_catch_all,disabled_login,disabled_delivery
[email protected],,,,104857600,,,,
[email protected],,,Internal,102400000,,,,
[email protected],,,,102400000,,,, kishorepr,xyz.com,,,,209715200,,,,

1 Answer 1

2

When a row contains the domain key, you retrieve the domain to get its key, but you don't update your row with the domain id.

Then when you do:

account = EmailAccount(**row)

the row object still has the key domain associated with the domain name. Since your EmailAccount class uses the name domain for the relationship, the db thinks it will get a Domain object when in fact, it's getting a string (the name). That is why you get the error AttributeError: 'str' object has no attribute '_sa_instance_state'.

Update: this should work

for row in csv_file:
    account_values = {}
    for key, value in row.items():
        if key == 'domain':
            mdomain = Domain.query.filter_by(domain_name = value).first()
            account_values['domain'] = mdomain
        else:
            account_values[key] = value
    account = EmailAccount(account_values)
    db.session.add(account)
    db.session.commit()
Sign up to request clarification or add additional context in comments.

5 Comments

@stackoverflow.com/users/1193366/morphyn Thanks for the quick response. Can you tell me the code changes to fix the same. Thanks in advance
Could you post an extract of your accounts csv file ?
stackoverflow.com/users/1193366/morphyn pasted the extract of the csv above.
@stackoverflow.com/users/1193366/morphyn I am unable to save the domain in the emailaccount model. It saves as None. Note: I am populating the emailaccount model using account = EmailAccount(**row), after changing the code for Domain as row['domain'] = Domain.query.filter_by[domain_name=domain_str].first(). The domain_str is split value taken from another csv with a key 'Email Account' which is a combination of username@domain_name
Noticed that I had not passed the foreignkey id value at all as 1 of the keyword. Now after adding it, it works well! Thx again.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.