Search code examples
djangodjango-celerycelery-task

how to return a django view from within a celery task?


I need to return a form from inside a django celery task. My task is called from the following django view:

class MyView(CreateView):
    model = MyModel
    form_class = MyForm
    success_url = '/create/form'

    def form_valid(self, form):
        form.save()
        # I call my task in here
        period.delay()
        print("Loading task...")

        return super(MyView, self).form_valid(form)

The name of my task is "period" and It compares dates with the objective to open a event while the condition in my IF is true. My "event" is a formulary that user has to confirm presence.

My task:

from .views import MyAnotherView 
# others imports...

"""
    in my settings.py, I had to call tha task every minute:
    CELERYBEAT_SCHEDULE = {
    'add-periodic-events': {
        'task': 'myapp.tasks.period',
        'schedule': crontab(minute='*'),
        }
    }
"""

@shared_task(serializer='json')
def period():
    event = MyModel.objects.get(id=1) # I limited my model to receive only one object

    request = RequestFactory.get('/another/form') 
    view = MyAnotherView() 

    week_d = week_day(event.day) # day is a field of my model
    event_d = event_day(week_d, event.hour) # hour is a field of my model
    conf_d = presence_confirm(event.before_days, event.begin_hour, event_d) # before_days and begin_hour are fields of my model

    utc_now = pytz.utc.localize(datetime.utcnow())
    n = utc_now.astimezone(pytz.timezone('America/Recife'))
    t_str = '{}-{}-{}'.format(n.year, n.month, n.day)
    t_hour = ' {}:{}'.format(n.hour, n.minute)

    today = t_str + t_hour
    today = datetime.strptime(today, '%Y-%m-%d %H:%M')

    if (today >= conf_d) and (today < event_d):
        # HOW TO CALL MY FORMULARY???
        print("Call my formulary")
        view.setup(request)

    else:
        # another thing

The formulary that I want to show after call my task and while the condition is true It will come of the django Model following:

class MyAnotherModel(models.Model):
    OPTIONS = (
        (True, 'Sim'),
        (False, 'Não'),
    )

    player = models.OneToOneField(MyUserModel, primary_key=True, on_delete=models.CASCADE)
    confirm = models.BooleanField(choices=OPTIONS, default=False)
    modified = models.DateTimeField(auto_now_add=True)

In short, I want my formulary appears while my condition is true. So for, I had try using, into my task, RequestFactory.get to catch the URL and call the view (MyAnotherView).

     # ...
     request = RequestFactory.get('/another/form') 
     view = MyAnotherView() # View responsável em instanciar MyAnotherModel 
     # ...
     if (today >= conf_d) and (today < event_d):
        view.setup(request)

However, I had receive ImportError: cannot import name 'MyAnotherView'. If anyone can help me, I aprecciate!


Solution

  • My problem was checking if today's date is between two dates. If so, I must "show" a form. This check I did with celery. And my question here was how I could do as long as today's date was between the dates to enable the form. I solved the problem as follows:

    • My celery task is not running within a request cycle since it's asynchronous - @dirkgroten - I soon thought of creating a new field in my django model (MyModel):
    enable = models.BooleanField(default=False)
    
    • Update this field within my task:
        # ...
        if (today >= conf_d) and (today < event_d):
            event.enable = True
            event.save()
    
        else:
            event.enable = False
            event.save()
    
    • Return this information as context within my template from my form view
        # In MyAnotherView
        model = MyAnotherModel
        fields = ['player', 'confirm']
        success_url = '/list/users'
        def get_context_data(self, **kwargs):
            context = super().get_context_data(**kwargs)
            obj = MyModel.objects.get(id=1)
            context['enable'] = obj.enable
            return context
    
    <!--This is the template that shows form-->
    {% if enable %}
        {{ form }}
    {% else %}
        <h2> Don't show <h2>
    {% endif %}
    

    If anyone thinks there is a better way, they can suggest me, but I will have that answer for now.