oTree Forum >

ExtraModel has no attribute 'objects'

#1 by qzyyb123

Hi there,

I am trying to run some live page codes which could be used in the previous otree version. However, when I try to replicate it, it fails.
receiving data...
receiving proposal...
recording history...
Exception in ASGI application
Traceback (most recent call last):
  File "d:\otree\project\venv\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 "d:\otree\project\venv\lib\site-packages\uvicorn\middleware\proxy_headers.py", line 45, in __call__
    return await self.app(scope, receive, send)
  File "d:\otree\project\venv\lib\site-packages\starlette\applications.py", line 112, in __call__
    await self.middleware_stack(scope, receive, send)
  File "d:\otree\project\venv\lib\site-packages\starlette\middleware\base.py", line 21, in __call__
    await self.app(scope, receive, send)
  File "d:\otree\project\venv\lib\site-packages\otree\errorpage.py", line 214, in __call__
    await self.app(scope, receive, send)
  File "d:\otree\project\venv\lib\site-packages\starlette\middleware\base.py", line 21, in __call__
    await self.app(scope, receive, send)
  File "d:\otree\project\venv\lib\site-packages\starlette\middleware\sessions.py", line 75, in __call__
    await self.app(scope, receive, send_wrapper)
  File "d:\otree\project\venv\lib\site-packages\otree\patch.py", line 16, in __call__
    await self.app(scope, receive, send)
  File "d:\otree\project\venv\lib\site-packages\starlette\routing.py", line 582, in __call__
    await route.handle(scope, receive, send)
  File "d:\otree\project\venv\lib\site-packages\starlette\routing.py", line 299, in handle
    await self.app(scope, receive, send)
  File "d:\otree\project\venv\lib\site-packages\starlette\endpoints.py", line 74, in dispatch
    raise exc from None
  File "d:\otree\project\venv\lib\site-packages\starlette\endpoints.py", line 68, in dispatch
    await self.on_receive(websocket, data)
  File "d:\otree\project\venv\lib\site-packages\otree\channels\consumers.py", line 126, in on_receive
    await self.post_receive_json(data, **self.cleaned_kwargs)
  File "d:\otree\project\venv\lib\site-packages\otree\channels\consumers.py", line 193, in post_receive_json
    await live_payload_function(
  File "d:\otree\project\venv\lib\site-packages\otree\live.py", line 45, in live_payload_function
    retval = call_live_method_compat(live_method, player, payload)
  File "d:\otree\project\venv\lib\site-packages\otree\live.py", line 99, in call_live_method_compat
    return player.call_user_defined(live_method, payload)
  File "d:\otree\project\venv\lib\site-packages\otree\database.py", line 627, in call_user_defined
    return func(self, *args, **kwargs)
  File "D:\Otree\Project\Software\negotiation\models.py", line 184, in live_negotiation
    Proposal.objects.create(player=self,
AttributeError: type object 'Proposal' has no attribute 'objects'

The code is:

models.py
class Player(BasePlayer):
    skippedWaitPage = models.BooleanField(initial=False)
    treatment = models.StringField()
    first_move = models.BooleanField()
    first_offer = models.CurrencyField()
    match_id = models.IntegerField()
    match_gender = models.StringField()

    def chat_nickname(self):
        return '{}'.format(self.participant.vars["role"])

    def live_negotiation(self, data):
        print("receiving data...")
        # type of data sent from page
        t = data["type"]
        # proposal sent?
        if t == "proposal":
            proposal = data["proposal"]
            # if proposal is valid (less than total revenue)...
            if proposal <= self.group.total_revenue:
                print("receiving proposal...")
                # is this the first proposal? if so, record variables
                if self.group.num_proposals == 0:
                    self.first_move = True
                    self.first_offer = c(proposal)
                    self.get_others_in_group()[0].first_move = False
                # record now most recent proposal
                self.group.num_proposals += 1
                if self.participant.vars["role"] == "Worker":
                    self.group.workers_current_proposal = c(proposal)
                else:
                    self.group.managers_current_proposal = c(proposal)
                # record history of proposals
                print("recording history...")
                Proposal.objects.create(player=self,
                                        group=self.group,
                                        role=self.participant.vars["role"],
                                        proposal=proposal,
                                        secs=time.time()-self.participant.vars["timeStartedNegotiation"])
        # proposal or connect?
        if t in ["proposal", "connect"]:
            # return response to page
            proposals = []
            for i in Proposal.objects.filter(group=self.group).order_by('id'):
                proposals.append([i.role, i.proposal])
            response = dict(type="proposals", proposals=proposals)
            return {0: response}
        # wage accepted
        elif t == "accept":
            print("wage accepted...")
            # record accepted worker's wage
            if self.group.final_workers_wage is None:
                if self.participant.vars["role"] == "Worker":
                    self.group.worker_accepted = True
                    self.group.final_workers_wage = self.group.managers_current_proposal
                    self.group.final_managers_profit = self.group.total_revenue - self.group.final_workers_wage
                else:
                    self.group.manager_accepted = True
                    self.group.final_workers_wage = self.group.workers_current_proposal
                    self.group.final_managers_profit = self.group.total_revenue - self.group.final_workers_wage
            # submit page for all
            response = dict(type="negotiation_finished")
            return {0: response}

    def waiting_too_long(self):
        return time.time() - self.participant.vars['wait_page_arrival'] > 4*60

class Proposal(models.ExtraModel):
    player = models.Link(Player)
    group = models.Link(Group)
    role = models.StringField()
    proposal = models.CurrencyField()
    secs = models.StringField()


def custom_export(players):
    # header row
    yield ["session.code", "participant.code", "player.id_in_group", "group.id_in_subsession",
           "role", "proposal", "secs"]
    # other rows
    for proposal in Proposal.objects.order_by('id'):
        player = proposal.player
        group = proposal.group
        participant = player.participant
        yield [player.session.code, participant.code, player.id_in_group, group.id_in_subsession,
               participant.vars["role"], proposal.proposal, proposal.secs]

pages.py
class Negotiation(Page):
    timeout_seconds = 4*60
    live_method = 'live_negotiation'

    def js_vars(self):
        return dict(
            max=self.group.total_revenue,
            my_role=self.participant.vars["role"]
        )

    def is_displayed(self):
        return self.player.skippedWaitPage is not True

    def vars_for_template(self):
        return dict(
            nickname=self.player.chat_nickname(),
            my_role=self.participant.vars["role"],
            other_role=self.player.get_others_in_group()[0].participant.vars["role"],
            other_gender=self.player.get_others_in_group()[0].participant.vars["gender"],
            other_birthSeason=self.player.get_others_in_group()[0].participant.vars["birthSeason"],
            other_eyeColour=self.player.get_others_in_group()[0].participant.vars["eyeColour"],
            other_siblings=self.player.get_others_in_group()[0].participant.vars["siblings"],
            total_revenue=self.group.total_revenue,
            treatment=self.participant.vars['treatment']
        )

    def before_next_page(self):
        # did they decide in time?
        if self.timeout_happened:
            self.group.agreement = False
            self.group.timeout_happened = True
            if self.participant.vars["role"] == "Worker":
                self.group.final_workers_wage = self.participant.vars["contributionsNoise"] * 0.2
                self.group.final_managers_profit = self.player.get_others_in_group()[0].participant.vars["contributionsNoise"] * 0.2
            else:
                self.group.final_workers_wage = self.player.get_others_in_group()[0].participant.vars["contributionsNoise"] * 0.2
                self.group.final_managers_profit = self.participant.vars["contributionsNoise"] * 0.2
        else:
            self.group.agreement = True
        # set payoffs
        if self.participant.vars["role"] == "Worker":
            self.player.payoff += self.group.final_workers_wage
        else:
            self.player.payoff += self.group.final_managers_profit

Any suggestions are greatly appreciated!

#2 by Chris_oTree ,

See ExtraModel documentation here: https://otree.readthedocs.io/en/latest/misc/advanced.html#extramodel

Change Proposal.objects.create() to Proposal.create(). There is also Proposal.filter(). And no more .order_by() etc.

#3 by qzyyb123

Thanks Chris! I've fixed it.

Write a reply

Set forum username