My model is defined as follows. Note that the ID column uses a server-side sequence.
class AgreementT(Base):
__tablename__ = 'agreement_t'
__table_args__ = {'schema': 'ets'}
id = Column(Integer, primary_key=True, server_default=text("nextval('ets.agreement_t_id_seq'::regclass)"))
agreement_number = Column(Numeric)
year = Column(Numeric(4, 0), nullable=False)
...
When I want to insert a record using Flask-SqlAlchemy, I need to fill out my model object first. But how do I specify that the ID column should be the default specified sequence?
#bp.route('submitAgreementForm', methods=['POST'])
def insertAgreementForm():
req_json = request.get_json()
form = req_json['form_values']
# Prepare model object
agreement = AgreementT(None, 'field1', 'field2', ...) # is 'None' correct for ID column?
Also, there is no default constructor for the model defined in Agreement.py. Is that ok?
Related
In models.py I have the following class that I would like to create a single instance of it while the application is initializing so that when I view my database one item with the name name is in the database without me doing anything
class Item(db.Model , UserMixin):
__tablename__ = 'items'
id = db.Column(db.Integer , primary_key=True)
name = db.Column(db.String, default="name")
You could write a simple query that runs on startup after db.create_all() that checks whether there are any rows
in the items table and adds one if there are none.
num_items = db.session.query(Item).count()
if not num_items:
item = Item() # Add parameters for UserMixin columns values if needed
db.session.add(item)
db.session.commit()
I am building an admin dashboard for my web app using Flask-Admin. For the user/address relationship, I am using a one to one relationship. On the user edit form, I'd like to be able to edit the individual components of the address (i.e. street address, city or zip) similar to what inline_models provides. Instead, flask-admin generates a select field and only allows me to select a different addresses.
I tried using inline_models = ['address'] in the UserModelView definition. However, I got the address object not iterable error due to the user/address relationship being configured to uselist=False. Switching uselist to True would affect other parts of my code, so I'd prefer to leave it as False.
From looking in flask-admin/contrib/sqla/forms, within the function get_forms, its being assigned a one to many tag which is what drives the use of a select field.
Before diving in further, I figured it best to see if anyone else has come across this or has a recommended fix/workaround.
models.py
class User(db.Model):
id = db.Column(db.Integer, primary_key=True)
username = db.Column(db.String(64))
address = db.relationship("Address", backref="user",
cascade="all, delete-orphan", lazy=False,
uselist=False, passive_deletes=True)
class Address(db.Model):
id = db.Column(db.Integer, primary_key=True)
line1 = db.Column(db.String(128))
zip = db.Column(db.String(20), index=True)
city = db.Column(db.String(64), index=True, nullable=False)
user_id = db.Column(db.Integer, db.ForeignKey("user.id",
ondelete="CASCADE"))
admin.py
class UserModelView(ModelView):
column_list = [User.username, 'address']
form_columns = (User.username, 'address')
admin = Admin(name='Ask', template_mode='bootstrap3')
admin.add_view(UserModelView(User, db.session))
You can create 2 relations
# Relation for flask admin inline model
address_cms_relationsip = db.relationship(
"Address", backref="user", cascade="all, delete-orphan", lazy=False,
uselist=True, passive_deletes=True)
address_relationship = db.relationship(
"Address", cascade="all, delete-orphan", lazy=False,
uselist=False, passive_deletes=True)
#property
def address(self):
return self.address_relationship
In your code you can use property address
user: User # some User object
user.address.city
I have 3 models: Document, MetaData, MetaDataValue, and a "connector table" DocumentMetaDataValue. I want to add a document and all the associated metadata values on one admin page.
models.py:
# MetaData
class MetaData(models.Model):
metadata_id = models.AutoField(primary_key = True)
name = models.CharField('metadata name', max_length=200, unique=True)
description = models.TextField('description')
def __str__(self):
return self.name
# MetaData Value
class MetaDataValue(models.Model):
metadata_id = models.ForeignKey(MetaData, on_delete=models.CASCADE,)
value = models.CharField('value', max_length=200, unique=True)
def __str__(self):
return self.value
# Document
class Document(models.Model):
document_id = models.AutoField(primary_key=True)
metadata = models.ManyToManyField('MetaData', through='DocumentMetaDataValue', through_fields=('document', 'metadata'))
metadatavalue = models.ManyToManyField('MetaDataValue', through='DocumentMetaDataValue', through_fields=('document', 'metadataValue'))
class DocumentMetaDataValue(models.Model):
document = models.ForeignKey(Document, on_delete=models.CASCADE)
metadata = models.ForeignKey(MetaData, on_delete=models.CASCADE)
metadataValue = models.ForeignKey(MetaDataValue, on_delete=models.CASCADE)
admin.py:
class Fred(forms.ModelForm):
""" something newer
"""
class Meta:
model = DocumentMetaDataValue
fields = '__all__'
class DocumentMetaDataValueInline(admin.TabularInline):
model = DocumentMetaDataValue
form = Fred
class DocumentAdmin(admin.ModelAdmin):
form = DocumentForm
list_display = ('get_document_type', 'document_state', 'title', 'description', 'original_file_name', 'created', 'storage_file_name', 'get_thumb', )
ordering = ('created',)
readonly_field = ('document_state',)
filter_horizontal = ('metadata', 'metadatavalue', )
inlines = (DocumentMetaDataValueInline, )
# other code not related to this problem
admin.site.register(Document, DocumentAdmin)
There are 16+ metadata names, and each one has one or more metadata values. For example, the metadata name Person has 23 values ('Bob', 'Sam', 'Sally', etc), and the metadata name Decade has 12+ values (1890, 1900, 1910, 1920, etc). A Document can have one or more metadata names associated with it, and one or more metadata values for that metadata name can be associated with that Document. For example, a Document can be a photograph with two people in it. The metadata value for Decade could be 1910, and the metadata values for Person could be Sam and Sally.
On each line of the inline, I want to show the metadata name opposite a drop down with the associated metadata values. One should be able to pick more than one metadata value for a particular metadata name.
What happens in the code above is each line of the inline has two drop down lists - the first is all the metadata names and the second is all the metadata values.
I want to change the first drop down to be a single string for a metadata name, and the second drop down to still be a drop down, but only the values for that metadata name. I don't see how to make these changes.
Thanks!
Mark
What is the best way to limit only one record to be default in django
I have a model where i have a flag for default
class BOMVersion(models.Model):
name = models.CharField(max_length=200,null=True, blank=True)
material = models.ForeignKey(Material)
is_default = models.BooleanField(default=False)
I want to have only one value to be default for the same material but this material can have a lot of non default ones.
It was odd that this question was not addressed more often. If I have a default record, I want to record that in the class as a member variable. And to determine if an instance is the default, I want to compare the class default member variable with the instance id. Unfortunately I could not figure out how to access class variables and instance variables nicely in the same class function in Python (may be someone can comment), but this does not require to hit the database for a default or store a bunch of records pointing to a default. Just one member variable in the class.
After I wrote this, I realized every time the application is restarted, default is reset to None, so you will have to store this in a database. I have updated my answer accordingly. However, checking that the member variable is not null, and only hitting the database if it is would reduce hits here. The model I used was:
class RecordOfInterest(models.Model):
"""
Record Records of interest here. Stores ID, and identifying character
"""
# asume maximum 64 character limit for the model.
model_name = models.CharField(max_length=64, unique=True)
record_no = models.IntegerField()
human_ident = models.CharField(max_length=64, help_text='How is this of interest')
# Id it as default, deposit, ... Don't bother indexing, as there will only be a few
def __unicode__(self):
return u'Model %s record %d for %s' % (self.model_name, self.record_no, self.human_ident)
class Meta:
unique_together = ('model_name', 'human_ident')
class Product(models.Model):
"""
Allow one product record to be the default using "Product.default = prod_instance"
check default with "Product.is_default(prod_instance)"
"""
default = None # set this to id of the default record
cart_heading = models.CharField(max_length=64, blank=True)
country = CountryField()
pricing = models.ForeignKey(
'Price', blank=True, null=True, related_name='visas', on_delete=models.SET_NULL)
#classmethod
def is_default(cls, obj):
if cls.default_no == None:
try:
cls.default_no = RecordOfInterest.objects.get(model_name=cls.__name__, human_ident='default')
except RecordOfInterest.DoesNotExist:
default_no = None
return cls.default_no == obj.id
#classmethod
def set_default(cls, obj):
try:
default_rec = RecordOfInterest.objects.get(model_name=cls.__name__, human_ident='default')
except RecordOfInterest.DoesNotExist:
RecordOfInterest.objects.create(model_name=cls.__name__, record_no=obj.id, human_ident='default')
else:
if default_rec.record_no != obj.id:
default_rec.record_no = obj.id
default_rec.save()
cls.default_no = obj.id
return
Saving the ID in settings.py if it is static.
Save it into a separate "default" table with one record (or use the most recent) if it's dynamic.
Save the default in another table like this:
class BOMVersion(models.Model):
name = models.CharField(max_length=200,null=True, blank=True)
material = models.ForeignKey(Material)
class BOMVersionDefault(model.Models)
time_set= models.Datetime(auto_created=True)
default_material = models.ForiegnKey(Material)
To query:
default = BOMVerDefault.objects.latest(time_set).get().default_material
If you have several material types that each need a default then default_material would be a field in a material-type table.
Getting one record to be default in a table is most basic requirement we developer come face to face, after spending couple of hours over it, i think a neat and clean solution in django would be update all records to default false if current form instance has default value to be "true" and then save the record.
class FeeLevelRate(TimeStampedModel):
"""
Stores the all the fee rates depend on feelevel
"""
feelevel = models.ForeignKey(FeeLevel, on_delete= models.PROTECT)
firstconsultfee = models.DecimalField(_('First Consultation Charges'),max_digits=10,decimal_places=2,blank=True)
medcharges = models.DecimalField(_('Medicines Charges per Day'),max_digits=10,decimal_places=2,blank=True)
startdate = models.DateField(_("Start Date "), default=datetime.date.today)
default_level = models.BooleanField(_('Is Default Level?'),default=False)
class Meta:
constraints = [
models.UniqueConstraint(fields=["feelevel","startdate"], name='unique_level_per_date'),
]
def __str__(self):
return "%s applicable from (%s)" % ( self.feelevel, self.startdate.strftime("%d/%m/%Y"))
class FeeLevelRateCreate(CreateView):
model = FeeLevelRate
fields = ['feelevel', 'firstconsultfee', 'medcharges', 'startdate', 'default_level']
context_object_name = 'categories'
success_url = reverse_lazy('patadd:feerate_list')
def form_valid(self, form):
# Update all the default_level with false.
#UserAddress.objects.filter(sendcard=True).update(sendcard=False)
if form.instance.default_level:
FeeLevelRate.objects.filter(default_level=True).update(default_level=False)
return super().form_valid(form)
I'm building a website that has several survey forms.
class A(Form):
name = Text('Name')
class B(Form):
name = Text('Name')
class C(Form):
name = Text('Name')
etc...
I have a model that holds information on all these forms:
class forms(db.Model):
id = db.Column(db.Integer(), primary_key=True)
name = db.Column(db.String(64), unique=True)
description = db.Column(db.String(255))
wtform = ???db.Column(db.String(255))??? # How to store a wtform [A, B, C, etc...]?
From a menu a user would select a particular form and be directed to:
#app.route('/new/<int:id>', methods = ['GET', 'POST'])
#login_required
def new(id):
data = forms.query.get(id)
form = data.???wtform??? # How to call the particular wtform here?
I'm not sure how to store and call a wtform class from sqlalchemy. What is the best method to deal with this sort of situation? Should I instead use multiple #app.route('/A'), #app.route('/B'), #app.route('/C') calls for each form, which would increase redundancy, or could I store the entire wtform class structure in the database?
You already define the forms in your code, so there's no reason to store them in the database as well. Store a reference to which form to use, then just instantiate the correct form based on the reference.
class FormDef(Base):
__tablename__ = 'form_def'
id = Column(Integer, primary_key=True)
name = Column(String, nullable=False, unique=True)
description = Column(String, nullable=False, default='')
form_ref = Column(String, nullable=False) # will be the name of the form class in this example
def get_form(instance):
# given an instance of FormDef, get the form class with the name stored in form_ref
from my_package import forms # or wherever your forms are stored
return getattr(forms, instance.form_ref)
On a side note, storing form class information in rows does not seem like the right way to solve whatever it is you're trying to solve.