Trying to do list:reference returns error
i was trying to do a list:reference to a table however i noticed that the has_many isn't an actual field(which is needed for it to go into a form). whenever i tried adding a list of references to refers_to it also threw an error:
TypeError: int() argument must be a string, a bytes-like object or a real number, not 'list'
full traceback:
Traceback (most recent call last):
File "/usr/local/lib/python3.11/site-packages/emmett/rsgi/handlers.py", line 203, in dynamic_handler
http = await self.router.dispatch(request, response)
File "/usr/local/lib/python3.11/site-packages/emmett/routing/router.py", line 249, in dispatch
return await match.dispatch(reqargs, response)
File "/usr/local/lib/python3.11/site-packages/emmett/routing/dispatchers.py", line 72, in dispatch
rv = self.response_builder(await self.f(**reqargs), response)
File "/usr/local/lib/python3.11/site-packages/emmett/pipeline.py", line 328, in flow
output = await pipe_method(f, **kwargs)
File "/usr/local/lib/python3.11/site-packages/emmett/pipeline.py", line 234, in pipe
return await next_pipe(**kwargs)
File "/usr/local/lib/python3.11/site-packages/emmett/pipeline.py", line 328, in flow
output = await pipe_method(f, **kwargs)
File "/usr/local/lib/python3.11/site-packages/emmett/tools/auth/apis.py", line 277, in pipe
return await next_pipe(**kwargs)
File "/usr/local/lib/python3.11/site-packages/emmett/pipeline.py", line 369, in flow
return await pipe_method(f, **kwargs)
File "/usr/local/lib/python3.11/site-packages/emmett/pipeline.py", line 274, in pipe_request
return await next_pipe(**kwargs)
File "/app/testem/controllers/microlearnings.py", line 27, in microlearning_content
grid = await SQLFORM.grid(query, GridSettings())
File "/app/testem/tools/sqlgrid.py", line 129, in grid
return await cls.__insert_form(query_helper, grid_settings)
File "/app/testem/tools/sqlgrid.py", line 152, in __insert_form
form = await model.form()
File "/usr/local/lib/python3.11/site-packages/emmett/forms.py", line 399, in _process
await super()._process(write_defaults=False)
File "/usr/local/lib/python3.11/site-packages/emmett/forms.py", line 165, in _process
self._validate_input()
File "/usr/local/lib/python3.11/site-packages/emmett/forms.py", line 383, in _validate_input
record.update(fields)
File "/usr/local/lib/python3.11/site-packages/emmett/orm/objects.py", line 1438, in update
self.__setattr__(key, val)
File "/usr/local/lib/python3.11/site-packages/emmett/orm/objects.py", line 1381, in __setattr__
object.__setattr__(self, key, value)
File "/usr/local/lib/python3.11/site-packages/emmett/orm/models.py", line 1158, in __set__
val = typed_row_reference(val, self.table)
File "/usr/local/lib/python3.11/site-packages/emmett/orm/helpers.py", line 558, in typed_row_reference
return {
File "/usr/local/lib/python3.11/site-packages/emmett/orm/helpers.py", line 121, in __new__
rv = super().__new__(cls, id, *args, **kwargs)
TypeError: int() argument must be a string, a bytes-like object or a real number, not 'list'
is there any way to get a list of references without using has_many or can you make it so has_many works in forms?
@gi0baro having a list:reference is pretty essential for what i'm currently making and without this i really can't move foward and can't continue using emmett...
@SvenKeimpema I'm sorry this blocks you, but there's really not so much Emmett can do for you at the moment on many relations and forms.
The proper way at the moment would be to manually handle the multiple forms on your own, and the relevant add/remove logic in the template as well. Note that you should be able to make all the forms you want in a single page, eg:
@app.route()
async def my_forms():
form = await ModelA.form()
form_related = await ModelB.form()
return locals()
in the upper code, you can manually set the ModelA reference using a custom onvalidation on the form of ModelB.
Without further information about your use-case I'm afraid there's no other option here.
@gi0baro what i currenly mean is that if i have a form for say ModelA.form() and i want it to have many references to ModelB like:
class ModelB(Model):
table_name="model_b"
random_field = Field.text(...)
class ModelA(Model):
refers_to({'modelb_ids', 'model_b'})
validation = {
'modelb_ids': {
'in': {
'dbset': lambda db: db.where(db.model_b.id>=0),
'orderby': lambda row: row.random_field,
'label_field': 'random_field'
},
'multiple': True
}
}
this will return an error if i insert [1, 2](which are the id's of modelB) into ModelA, For example:
ModelB.create(random_field="1")
ModelB.create(random_field="2")
ModelA.create(modelb_ids=[1, 2])
(it will throw the error i typed above)
@SvenKeimpema yes, that's correct. As per documentation refers_to and belongs_to are 1:1 relationships.
1:N relations are provided by has_many, and considering what you described, you probably want to make the inverse relationship:
class ModelA(Model):
has_many({'modelb_ids': 'ModelB'})
class ModelB(Model):
refers_to({'model_a': 'ModelA'})
random_field = Field.text()
so then you can:
a = ModelA.create()
ModelB.create(random_field="1", model_a=a.id)
ModelB.create(random_field="2", model_a=a.id)
@gi0baro not exactly, yes i want a N->1 relation however if i use has_many in a form
ModelA.form()
it will not show the has_many field in the form, which in the form i want to be able to select relations to said form. for example if there are 3 fields in ModelB
ModelB.create(random_field="1", model_a=a.id)
ModelB.create(random_field="2", model_a=a.id)
ModelB.create(random_field="3", model_a=a.id)
i want to be able to select those fields in the form when i am in ModelA. Like when i am on the form i want to be able to select the reference random_field 1 and random field 2. AKA modelA will make 2 relations to modelB
@SvenKeimpema I get your point, the thing IMHO is that you need to decide which side of the 1:N relationship is 1 and which one is N.
If the relationship is 1:N for ModelA:ModelB, and you want to create ModelB from ModelA in forms you have 2 options with Emmett 2.5:
- use
list:referenceField (even if not supported directly by Emmett directly, but only as an historical feature coming from pyDAL) which also means you already haveModelBrecords existing out there - use
has_manyandrefers_tothe way they are intended to be used, and develop your own multi-form solution to support it. There's no planned future support to do this in a single form, as you're actually managing several records into one, and thus you'll need multiple forms to do that, and it would be quite hard for Emmett itself to know exactly your specific use-case in advance. As you might want to create N -> inf number ofModelBrecords, for sure this will require dynamic forms generation and for sure a bit of javascript to get it working, which is something Emmett's form cannot take for granted..
If you end-up with a general-purpose solution for this, maybe you can create an Extension providing such components :)