#1 by papayapap (edited )
I am learning to use ExtraModel, so I created a simple example below. Unfortunately, it gives me the error in the title and I am not sure why. StackOverflow answers suggest this comes because an integer (ID) is being interpreted as an ORM object. Anybody knows that this means in an Otree context? ############### init.py #################### from otree.api import * class C(BaseConstants): NAME_IN_URL = 'store_data' PLAYERS_PER_GROUP = None NUM_ROUNDS = 1 NUM_EMPLOYERS = 3 NUM_WORKERS = 7 class Subsession(BaseSubsession): pass class Group(BaseGroup): is_finished = models.BooleanField(initial=False) job_id = models.IntegerField(initial=0) class Player(BasePlayer): is_employer = models.IntegerField() worker_is_employed = models.IntegerField(initial=0, min=0, max=1) employer_num_workers = models.IntegerField(initial=0, min=0, max=3) class JobOffer(ExtraModel): group = models.Link(Group) employer_id = models.Link(Player) wage = models.IntegerField(min=0, max=100) effort = models.IntegerField(min=0, max=10) # FUNCTIONS def creating_session(subsession: Subsession): players = subsession.get_players() if subsession.round_number == 1: for p in players: if p.id_in_group <= C.NUM_EMPLOYERS: p.participant.is_employer = True else: p.participant.is_employer = False for p in players: if p.participant.is_employer: p.is_employer = 1 else: p.is_employer = 0 # PAGES class MyPage(Page): form_model = 'group' @staticmethod def js_vars(player: Player): return dict(my_id=player.id_in_group, is_employer=player.is_employer) @staticmethod def live_method(player: Player, data): group = player.group my_id = player.id_in_group print('Received: ', data) print(my_id) if data['information_type'] == 'offer': # check if offer was legit. If yes... group.job_id = group.job_id + 1 print(group.job_id) employer_id = data['employer_id'] wage = data['wage'] effort = data['effort'] JobOffer.create( group=group, employer_id=employer_id, wage=wage, effort=effort, ) offer = JobOffer.filter(group=group) print(offer) class ResultsWaitPage(WaitPage): pass class Results(Page): pass page_sequence = [MyPage, ResultsWaitPage, Results] ######################## MyPage {{ block title }} <form> <div class="row"> <div class="col"> <input type="text" id="wage" class="form-control" placeholder="Wage"> </div> <div class="col"> <input type="text" id="effort" class="form-control" placeholder="Effort"> </div> <div class="col"> <button type="button" class="btn btn-secondary" id="send_offer" onclick="sendOffer()"> Send Offer </button> </div> </div> </form> <script> function sendOffer() { let offered_wage = parseInt(document.getElementById("wage").value); let effort_requested = parseInt(document.getElementById("effort").value); let my_id = parseInt(js_vars.my_id); } else { liveSend({ "information_type": "offer", "employer_id": my_id, "wage": offered_wage, "effort": effort_requested }); document.getElementById("effort").value = ""; document.getElementById("wage").value = ""; } } </script> {{ endblock }} Full error message: Exception in ASGI application Traceback (most recent call last): File "C:\Users\felix\AppData\Local\Programs\Python\Python39\lib\site-packages\uvicorn\protocols\websockets\websockets_impl.py", line 162, in run_asgi result = await self.app(self.scope, self.asgi_receive, self.asgi_send) File "C:\Users\felix\AppData\Local\Programs\Python\Python39\lib\site-packages\uvicorn\middleware\proxy_headers.py", line 45, in __call__ return await self.app(scope, receive, send) File "C:\Users\felix\AppData\Local\Programs\Python\Python39\lib\site-packages\starlette\applications.py", line 112, in __call__ await self.middleware_stack(scope, receive, send) File "C:\Users\felix\AppData\Local\Programs\Python\Python39\lib\site-packages\starlette\middleware\base.py", line 21, in __call__ await self.app(scope, receive, send) File "C:\Users\felix\AppData\Local\Programs\Python\Python39\lib\site-packages\otree\errorpage.py", line 214, in __call__ await self.app(scope, receive, send) File "C:\Users\felix\AppData\Local\Programs\Python\Python39\lib\site-packages\starlette\middleware\base.py", line 21, in __call__ await self.app(scope, receive, send) File "C:\Users\felix\AppData\Local\Programs\Python\Python39\lib\site-packages\starlette\middleware\sessions.py", line 75, in __call__ await self.app(scope, receive, send_wrapper) File "C:\Users\felix\AppData\Local\Programs\Python\Python39\lib\site-packages\otree\patch.py", line 16, in __call__ await self.app(scope, receive, send) File "C:\Users\felix\AppData\Local\Programs\Python\Python39\lib\site-packages\starlette\routing.py", line 582, in __call__ await route.handle(scope, receive, send) File "C:\Users\felix\AppData\Local\Programs\Python\Python39\lib\site-packages\starlette\routing.py", line 299, in handle await self.app(scope, receive, send) File "C:\Users\felix\AppData\Local\Programs\Python\Python39\lib\site-packages\starlette\endpoints.py", line 74, in dispatch raise exc from None File "C:\Users\felix\AppData\Local\Programs\Python\Python39\lib\site-packages\starlette\endpoints.py", line 68, in dispatch await self.on_receive(websocket, data) File "C:\Users\felix\AppData\Local\Programs\Python\Python39\lib\site-packages\otree\channels\consumers.py", line 127, in on_receive await self.post_receive_json(data, **self.cleaned_kwargs) File "C:\Users\felix\AppData\Local\Programs\Python\Python39\lib\site-packages\otree\channels\consumers.py", line 194, in post_receive_json await live_payload_function( File "C:\Users\felix\AppData\Local\Programs\Python\Python39\lib\site-packages\otree\live.py", line 45, in live_payload_function retval = call_live_method_compat(live_method, player, payload) File "C:\Users\felix\AppData\Local\Programs\Python\Python39\lib\site-packages\otree\live.py", line 101, in call_live_method_compat return live_method(player, payload) File "C:\Users\felix\Documents\Otree\test\testproject\store_data\__init__.py", line 81, in live_method JobOffer.create( File "C:\Users\felix\AppData\Local\Programs\Python\Python39\lib\site-packages\otree\database.py", line 912, in create db.add(obj) File "C:\Users\felix\AppData\Local\Programs\Python\Python39\lib\site-packages\otree\database.py", line 170, in add return self._db.add(obj) File "C:\Users\felix\AppData\Local\Programs\Python\Python39\lib\site-packages\sqlalchemy\orm\session.py", line 2023, in add self._save_or_update_state(state) File "C:\Users\felix\AppData\Local\Programs\Python\Python39\lib\site-packages\sqlalchemy\orm\session.py", line 2039, in _save_or_update_state for o, m, st_, dct_ in mapper.cascade_iterator( File "C:\Users\felix\AppData\Local\Programs\Python\Python39\lib\site-packages\sqlalchemy\orm\mapper.py", line 3098, in cascade_iterator queue = deque( File "C:\Users\felix\AppData\Local\Programs\Python\Python39\lib\site-packages\sqlalchemy\orm\relationships.py", line 1938, in cascade_iterator tuples = state.manager[self.key].impl.get_all_pending(state, dict_) File "C:\Users\felix\AppData\Local\Programs\Python\Python39\lib\site-packages\sqlalchemy\orm\attributes.py", line 967, in get_all_pending ret = [(instance_state(current), current)] AttributeError: 'int' object has no attribute '_sa_instance_state