Tortoise pydantic_model_creator method not contain ForeignKeyField of a model - fastapi

I have two models Article and Tag. Article has a foreign key from Tag
class Tag(MyAbstractBaseModel):
name = fields.CharField(max_length=255, index=True)
class Article(MyAbstractBaseModel):
title = fields.CharField(max_length=255, index=True)
body = fields.CharField(max_length=255)
tag = fields.ForeignKeyField(model_name="Tag", related_name='article', on_delete=fields.CASCADE)
Here I would like to have my response checked by the following
#router.get("/article", response_model=get_serialize_pydantic(Article))
async def get():
pass
Here is how I get the response model
from tortoise.contrib.pydantic import pydantic_model_creator
from pydantic import BaseModel, Field, create_model
def get_serialize_pydantic(models_obj: Type[Model], exclude: Tuple[str, ...] = None):
my_model_pydantic = pydantic_model_creator(models_obj)
response_pydantic = create_model(
__model_name=f"{models_obj.__name__}_response_pydantic",
data=(Optional[List[my_model_pydantic]], Field(None, title="data")),
__base__=BaseResponse)
return response_pydantic
Problem is I only title and body in my response model, there is no tag in it. Anyone knows why?

This is not an issue from pydantic side. It's pydantic_model_creator of tortoise that causes the problem.
The solution is simple. Just init before serialisation.
from tortoise import Tortoise
Tortoise.init_models(["__main__"], "models")
my_model_pydantic = pydantic_model_creator(models_obj)
https://tortoise-orm.readthedocs.io/en/latest/contrib/pydantic.html#relations-early-init

Related

Saving object creation datetime using Pynamodb

I want to update the creation_datetime only when a new object is created and update the last_update_datetime on every update while using the save method. default_for_new is updating time even when the existing object is updating. any alternatives?
below is the sample code I tried
from pynamodb.models import Model
from pynamodb.attributes import UTCDateTimeAttribute
def current_datetime():
from datetime import datetime
return datetime.now()
class AbstractDateTimeModel(Model):
creation_datetime = UTCDateTimeAttribute(default_for_new=current_datetime)
last_update_datetime = UTCDateTimeAttribute(default=current_datetime)
class Meta(object):
abstract = True
The best way you can do this is overriding the update method of your Object class. This is an example:
from datetime import datetime, timezone
from pynamodb.models import Model
from pynamodb.settings import OperationSettings
from pynamodb.attributes import UTCDateTimeAttribute, UnicodeAttribute, NumberAttribute
def get_current_time_utc():
return datetime.now(timezone.utc)
class SalesItem(Model):
class Meta:
table_name = 'monthlySales'
id = UnicodeAttribute(hash_key=True)
month= UnicodeAttribute()
sales= NumberAttribute()
createDateTime = UTCDateTimeAttribute(default_for_new=get_current_time_utc)
updateDateTime = UTCDateTimeAttribute(default_for_new=get_current_time_utc)
# overriding the method to add timestamp on update
def update(self, actions=[], condition=None, settings=OperationSettings.default):
actions.append(SalesItem.updateDateTime.set(get_current_time_utc()))
Model.update(self, actions, condition, settings)
We just need to append an action to the actions list asking the timestamp update. Then call the parent's update method. Now you can forget about the timestamps and use the update method as you would.
item = SalesItem.get('pynamodb-test')
# do your updates here
item.update(actions=[
SalesItem.sales.set(item.sales + 1)
])

Django CMS - Copy relations in custom plugin not working

I have a problem with copy_relations after publishing a page.
I have a plugin with additional model. This model has ForeignKey to my plugin.
class InfContactForm(CMSPlugin):
name = models.CharField(max_length=50)
def copy_relations(self, oldinstance):
self.inf_contact_form.all().delete()
for inf_contact_form in oldinstance.inf_contact_form.all():
inf_contact_form.pk = None
inf_contact_form.plugin = self
inf_contact_form.save()
class InfContactFormAgreement(models.Model):
inf_contact_form = models.ForeignKey(InfContactForm, related_name="inf_contact_form")
agreement = HTMLField(blank=True, null=True)
The "InfContactFormAgreement" model is then used as stacked inline in "InfContactForm" plugin form.
Like it is written in docs, there is also copy_relations() method but when the page is published, nothing happens. When I get back again to edit mode InfContactFormAgreement is doubled every time I do it.
Here is also my cms_plugins.py file:
class PluginInfContactForm(CMSPluginBase):
render_template = '_contact_form.html'
name = name1
model = InfContactForm
require_parent = False
inlines = [AgreementStackedInline, ]
def render(self, context, instance, placeholder):
context['instance'] = instance
context = super(PluginInfContactForm, self).render(context, instance, placeholder)
agreements = instance.inf_contact_form.all()
context.update({
'agreements': agreements,
})
return context
plugin_pool.register_plugin(PluginInfContactForm)
Thank you for any advice.
instead of:
inf_contact_form.plugin = self
you should use:
inf_contact_form.inf_contact_form = self
and the resulting code should look like this:
def copy_relations(self, oldinstance):
self.inf_contact_form.all().delete()
for inf_contact_form in oldinstance.inf_contact_form.all():
inf_contact_form.pk = None
inf_contact_form.inf_contact_form = self
inf_contact_form.save()
regards
I had a similar situation, the only difference being that my many-to-many relationship was not in a plugin, but an extension (PageExtension).
In my case, #Dariusz solution did not work, and I had to update the implicit "inbetween" model that exists between the Extension model and the Associated model.
My solution (the key being the "through"):
class Extension(PageExtension):
tags_regions = models.ManyToManyField(Region, related_name="articles", blank=True)
def copy_relations(self, oldinstance):
for region_tag in oldinstance.tags_regions.through.objects.filter(extension=oldinstance):
region_tag.extension = self
region_tag.save()

Mongoengine serialize dictionary (with nested dicts)?

I've created a dictionary from an Uploaded file in Django.
This dictionary has a nested list of dictionaries:
file = {"name": "filename", "sections": [{"section_name": "string", "lines": [{line_number: 0, "line"; "data"}]}], "etc": "etc"}
The model represents the dictionaries depth too.
class Line(EmbeddedDocument):
line_number = IntField()
line = StringField()
definition = ReferenceField(Definition)
class Section(EmbeddedDocument):
section_name = StringField()
lines = EmbeddedDocumentListField(Line))
class File(Document):
name = StringField()
sections = EmbeddedDocumentListField(Section))
created_on = DateTimeField()
created_by = StringField()
modified_on = DateTimeField()
modified_by = StringField()
In the POST I have the following to chop the file up into the above Dict (the file is a simple text file):
file= {}
with open(os.path.join(path, filename + ".txt"), 'r') as temp_file:
filelines = temp_file.readlines()
sections = []
section = {}
lines = []
for i, l in enumerate(filelines):
if i == 0:
section["section_name"] = "Top"
elif '*' in l:
if l.index('*') == 0 and '*' not in lines[len(lines) - 2"line"]:
section["lines"] = lines
lines = []
sections.append(section)
section = dict()
section["section_name"] = filelines[i + 1][1:-2]
line = {"line_number": i + 1, "line": l}
lines.append(line)
section['lines'] = lines
sections.append(section)
file["name"] = filename
file["sections"] = sections
I will tidy this up eventually.
Once the dict has been made how do I serialise it using the serializer?
Is it possible to insert this into a serializer?
If not how can I get it all into the database with validation?
I've tried json.dumps() and JsonRequst() then putting them in data= for the serializer but get Unable to get repr for <class '....'>
I'm pretty new to Django and MongoDB so if you need more info I can provide :)
Thanks!
Update
Change the model's List Fields to EmbeddedDocumentListField as suggest in the answer.
Answered
Thanks to Boris' suggestion below it pointed me to an error I wasn't getting initially. I had a typo and passing the dict directly into FileSerializer(data=file) works like a charm! :)
James!
The easiest way to validate that your incoming JSONs adhere to the Mongoengine Documents schema that you've specified is to use DRF-Mongoengine's DocumentSerializer.
Basically, what you need to do is create a serializer
serializers.py
import rest_framework_mongoengine
class FileSerializer(rest_framework_mongoengine.DocumentSerializer):
class Meta:
fields = '__all__'
model = File
Then you need a view or viewset that makes use of this Serializer to respond to GET/POST/PUT/DELETE requests.
views.py
from rest_framework_mongoengine import viewsets
class FileViewSet(viewsets.ModelViewSet):
lookup_field = 'id'
serializer_class = FileSerializer
def get_queryset(self):
return File.objects.all()
and register this viewset with a router
urls.py
from rest_framework import routers
# this is DRF router for REST API viewsets
router = routers.DefaultRouter()
# register REST API endpoints with DRF router
router.register(r'file', FileViewSet, r"file")
I'd also recommend using EmbeddedDocumentListField instead of ListField(EmbeddedDocumentField(Section)) - it has additional methods.

Flask-WTF: Queries of FormFields in FieldList are none after validate_on_submit

I'm trying to generate dynamic forms using Flask-WTF to create a new product based on some templates. A product will have a list of required key-value pairs based on its type, as well as a list of parts required to build it. The current relevant code looks as follows:
forms.py:
class PartSelectionForm(Form):
selected_part = QuerySelectField('Part', get_label='serial', allow_blank=True)
part_type = StringField('Type')
slot = IntegerField('Slot')
required = BooleanField('Required')
def __init__(self, csrf_enabled=False, *args, **kwargs):
super(PartSelectionForm, self).__init__(csrf_enabled=False, *args, **kwargs)
class NewProductForm(Form):
serial = StringField('Serial', default='', validators=[DataRequired()])
notes = TextAreaField('Notes', default='')
parts = FieldList(FormField(PartSelectionForm))
views.py:
#app.route('/products/new/<prodmodel>', methods=['GET', 'POST'])
#login_required
def new_product(prodmodel):
try:
model = db.session.query(ProdModel).filter(ProdModel.id==prodmodel).one()
except NoResultFound, e:
flash('No products of model type -' + prodmodel + '- found.', 'error')
return redirect(url_for('index'))
keys = db.session.query(ProdTypeTemplate.prod_info_key).filter(ProdTypeTemplate.prod_type_id==model.prod_type_id)\
.order_by(ProdTypeTemplate.prod_info_key).all()
parts_needed = db.session.query(ProdModelTemplate).filter(ProdModelTemplate.prod_model_id==prodmodel)\
.order_by(ProdModelTemplate.part_type_id, ProdModelTemplate.slot).all()
class F(forms.NewProductForm):
pass
for key in keys:
if key.prod_info_key in ['shipped_os','factory_os']:
setattr(F, key.prod_info_key, forms.QuerySelectField(key.prod_info_key, get_label='version'))
else:
setattr(F, key.prod_info_key, forms.StringField(key.prod_info_key, validators=[forms.DataRequired()]))
form = F(request.form)
if request.method == 'GET':
for part in parts_needed:
entry = form.parts.append_entry(forms.PartSelectionForm())
entry.part_type.data=part.part_type_id
entry.slot.data=slot=part.slot
entry.required.data=part.required
entry.selected_part.query = db.session.query(Part).join(PartModel).filter(PartModel.part_type_id==part.part_type_id, Part.status=='inventory')
if form.__contains__('shipped_os'):
form.shipped_os.query = db.session.query(OSVersion).order_by(OSVersion.version)
if form.__contains__('factory_os'):
form.factory_os.query = db.session.query(OSVersion).order_by(OSVersion.version)
if form.validate_on_submit():
...
Everything works as expected on a GET request, but on the validate_on_submit I get errors. The error is that all of the queries and query_factories for the selected_part QuerySelectFields in the list of PartSelectionForms is none, causing either direct errors in WTForms validation code or when Jinja2 attempts to re-render the QuerySelectFields. I'm not sure why this happens on the POST when everything appears to be correct for the GET.
I realized that although I set the required queries on a GET I'm not doing it for any PartSelectionForm selected_part entries on the POST. Since I already intended part_type, slot, and required to be hidden form fields, I added the following immediately before the validate_on_submit and everything works correctly:
for entry in form.parts:
entry.selected_part.query = db.session.query(Part).join(PartModel).\
filter(PartModel.part_type_id==entry.part_type.data, Part.status=='inventory')

how to add data from ui (made using pyqt) to sqlite db?

i have made a simple pyqt gui with QLineEdit and QPushButton. i like to add showurl(in con.py given below) to sqlite db. the code :
ui.py
from PyQt4 import QtCore, QtGui
try:
_fromUtf8 = QtCore.QString.fromUtf8
except AttributeError:
def _fromUtf8(s):
return s
try:
_encoding = QtGui.QApplication.UnicodeUTF8
def _translate(context, text, disambig):
return QtGui.QApplication.translate(context, text, disambig, _encoding)
except AttributeError:
def _translate(context, text, disambig):
return QtGui.QApplication.translate(context, text, disambig)
class Ui_Form(object):
def setupUi(self, Form):
Form.setObjectName(_fromUtf8("Form"))
Form.resize(400, 300)
self.verticalLayout_2 = QtGui.QVBoxLayout(Form)
self.verticalLayout_2.setObjectName(_fromUtf8("verticalLayout_2"))
self.verticalLayout = QtGui.QVBoxLayout()
self.verticalLayout.setObjectName(_fromUtf8("verticalLayout"))
self.inserturl = QtGui.QLineEdit(Form)
self.inserturl.setObjectName(_fromUtf8("inserturl"))
self.verticalLayout.addWidget(self.inserturl)
spacerItem = QtGui.QSpacerItem(20, 40, QtGui.QSizePolicy.Minimum, QtGui.QSizePolicy.Expanding)
self.verticalLayout.addItem(spacerItem)
self.insertdb_btn = QtGui.QPushButton(Form)
self.insertdb_btn.setObjectName(_fromUtf8("insertdb_btn"))
self.verticalLayout.addWidget(self.insertdb_btn)
self.verticalLayout_2.addLayout(self.verticalLayout)
self.retranslateUi(Form)
QtCore.QMetaObject.connectSlotsByName(Form)
def retranslateUi(self, Form):
Form.setWindowTitle(_translate("Form", "Form", None))
self.insertdb_btn.setText(_translate("Form", "ok", None))
con.py
import sys
from PyQt4 import QtCore, QtGui, QtSql
from insertdb2_ui import Ui_Form
def createConnection():
db = QtSql.QSqlDatabase.addDatabase('QSQLITE')
db.setDatabaseName('webscrap.db')
if db.open():
return True
else:
print db.lastError().text()
return False
class Test(QtGui.QWidget, Ui_Form):
def __init__(self):
super(Test, self).__init__()
self.setupUi(self)
self.insertdb_btn.clicked.connect(self.onClick)
def onClick(self):
showurl = self.inserturl.text()
print showurl
if __name__ == "__main__":
app = QtGui.QApplication(sys.argv)
if not createConnection():
sys.exit(1)
window = Test()
window.show()
sys.exit(app.exec_())
i made the input data ie. showurl print on terminal. i want to add the showurl to sqlitedb webscrap.db which has fields id and url.
I am also confused about using model view(QSqlTableModel, QDataWidgetMapper).
You can use the QtSql.QSqlQuery() class to create query objects that interact with your database. To execute a query (in this case, an INSERT query), perform the following steps:
Instantiate a QSqlQuery() object.
Call the prepare() method on that object, passing to the prepare() method the SQL text that you want to execute.
Use the addBindValue() method to pass in variables to your query -- in this case, you want to pass in showurl.
Finally, call exec_() on the query object to execute
the query. If there is an error, you can retrieve it by calling on
the query object lastError().text().
So, in code, you would need to change your onClick() function to:
def onClick(self):
showurl = self.inserturl.text()
query_object = QtSql.QSqlQuery()
query_object.prepare("INSERT INTO table (url) VALUES (?)")
query_object.addBindValue(showurl)
if not query_object.exec_():
print query_object.lastError().text()
Please note that I did not specify a table name to insert into because you provided field names (id and url) but not a table name; therefore the query string uses a fake table name (table). You should replace this with a real table name. If you do not have a table in your database, you need to create one. Please reference https://www.sqlite.org/lang.html for documentation on SQLite commands (e.g. for creating tables). Please also refer to the PyQt4 documentation on QtSql.QSqlQuery(): http://pyqt.sourceforge.net/Docs/PyQt4/qsqlquery.html
For QSqlTableModel and QDataWidgetMapper, it is difficult to help without having a specific question to answer about them, but I highly recommend that you check out this excellent guide to both of those classes: http://doc.qt.digia.com/qq/qq21-datawidgetmapper.html. This guide does not use PyQt4 (instead it uses Qt alone, so the code is in C++), but the code is sparse and the concepts should resonate even if you are not familiar with C++.

Resources