Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Trying to do list:reference returns error #472

Open
SvenKeimpema opened this issue Jul 5, 2023 · 6 comments
Open

Trying to do list:reference returns error #472

SvenKeimpema opened this issue Jul 5, 2023 · 6 comments
Labels

Comments

@SvenKeimpema
Copy link
Contributor

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?

@SvenKeimpema
Copy link
Contributor Author

SvenKeimpema commented Jul 6, 2023

@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...

@gi0baro
Copy link
Member

gi0baro commented Jul 6, 2023

@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.

@SvenKeimpema
Copy link
Contributor Author

@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)

@gi0baro
Copy link
Member

gi0baro commented Jul 6, 2023

@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)

@SvenKeimpema
Copy link
Contributor Author

SvenKeimpema commented Jul 6, 2023

@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

@gi0baro
Copy link
Member

gi0baro commented Jul 6, 2023

@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:reference Field (even if not supported directly by Emmett directly, but only as an historical feature coming from pyDAL) which also means you already have ModelB records existing out there
  • use has_many and refers_to the 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 of ModelB records, 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 :)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

2 participants