рд╕реЗрд▓реЗрд░реА рдХреЗ рд╕рд╛рде Django рдореЗрдВ рдЕрддреБрд▓реНрдпрдХрд╛рд▓рд┐рдХ рдиреМрдХрд░рд┐рдпрд╛рдВ

рдкрд╛рдпрдерди рд╡реЗрдм рдбреЗрд╡рд▓рдкрд░ рдкрд╛рдареНрдпрдХреНрд░рдо рдХреА рд╢реБрд░реБрдЖрдд рд╕реЗ рдкрд╣рд▓реЗ рд▓реЗрдЦ рдХрд╛ рдЕрдиреБрд╡рд╛рдж рддреИрдпрд╛рд░ рдХрд┐рдпрд╛ рдЧрдпрд╛ рдерд╛ ред



рдпрджрд┐ рдЖрдкрдХреЗ рдЖрд╡реЗрджрди рдореЗрдВ рдПрдХ рд▓рдВрдмреА рдкреНрд░рдХреНрд░рд┐рдпрд╛ рд╣реИ, рддреЛ рдЖрдк рдЗрд╕реЗ рдорд╛рдирдХ рдЕрдиреБрд░реЛрдз / рдкреНрд░рддрд┐рдХреНрд░рд┐рдпрд╛ рд╕реНрдЯреНрд░реАрдо рдореЗрдВ рдирд╣реАрдВ, рдмрд▓реНрдХрд┐ рдкреГрд╖реНрдарднреВрдорд┐ рдореЗрдВ рд╕рдВрд╕рд╛рдзрд┐рдд рдХрд░ рд╕рдХрддреЗ рд╣реИрдВред

рдЙрджрд╛рд╣рд░рдг рдХреЗ рд▓рд┐рдП, рдЖрдкрдХреЗ рдЖрд╡реЗрджрди рдореЗрдВ, рдЙрдкрдпреЛрдЧрдХрд░реНрддрд╛ рдХреЛ рдПрдХ рдердВрдмрдиреЗрд▓ рдЫрд╡рд┐ (рдЬреЛ, рд╕рдмрд╕реЗ рдЕрдзрд┐рдХ рд╕рдВрднрд╛рд╡рдирд╛ рд╣реИ, рд╕рдВрдкрд╛рджрд┐рдд рдХрд░рдиреЗ рдХреА рдЖрд╡рд╢реНрдпрдХрддрд╛ рд╣реЛрдЧреА) рдФрд░ рдИрдореЗрд▓ рдкрддреЗ рдХреА рдкреБрд╖реНрдЯрд┐ рдХрд░рдиреА рд╣реЛрдЧреАред рдпрджрд┐ рдЖрдкрдХрд╛ рдПрдкреНрд▓рд┐рдХреЗрд╢рди рдЫрд╡рд┐ рд╕рдВрд╕рд╛рдзрд┐рдд рдХрд░рддрд╛ рд╣реИ рдФрд░ рдлрд┐рд░ рдЕрдиреБрд░реЛрдз рд╣реИрдВрдбрд▓рд░ рдореЗрдВ рдкреБрд╖реНрдЯрд┐ рдХреЗ рд▓рд┐рдП рдПрдХ рдИрдореЗрд▓ рднреЗрдЬрддрд╛ рд╣реИ, рддреЛ рдЕрдВрддрд┐рдо рдЙрдкрдпреЛрдЧрдХрд░реНрддрд╛ рдХреЛ рдкреГрд╖реНрда рдХреЛ рдлрд┐рд░ рд╕реЗ рд▓реЛрдб рдХрд░рдиреЗ рдпрд╛ рдмрдВрдж рдХрд░рдиреЗ рд╕реЗ рдкрд╣рд▓реЗ рджреЛрдиреЛрдВ рдХрд╛рд░реНрдпреЛрдВ рдХреЛ рдкреВрд░рд╛ рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП рдХрд┐рд╕реА рдХрд╛рд░рдг рдХрд╛ рдЗрдВрддрдЬрд╛рд░ рдХрд░рдирд╛ рд╣реЛрдЧрд╛ред рдЗрд╕рдХреЗ рдмрдЬрд╛рдп, рдЖрдк рдЗрди рдХрд╛рд░реНрдпреЛрдВ рдХреЛ рдХрд╛рд░реНрдп рдХрддрд╛рд░ рдореЗрдВ рд╕реНрдерд╛рдирд╛рдВрддрд░рд┐рдд рдХрд░ рд╕рдХрддреЗ рд╣реИрдВ рдФрд░ рдЗрд╕реЗ рдЙрдкрдпреЛрдЧрдХрд░реНрддрд╛ рдХреЗ рд▓рд┐рдП рддреБрд░рдВрдд рдкреНрд░рддрд┐рдХреНрд░рд┐рдпрд╛ рднреЗрдЬрдиреЗ рдХреЗ рд▓рд┐рдП рдкреНрд░рд╕рдВрд╕реНрдХрд░рдг рдХреЗ рд▓рд┐рдП рдПрдХ рдЕрд▓рдЧ рдкреНрд░рдХреНрд░рд┐рдпрд╛ рдкрд░ рдЫреЛрдбрд╝ рд╕рдХрддреЗ рд╣реИрдВред рдЗрд╕ рд╕реНрдерд┐рддрд┐ рдореЗрдВ, рдЕрдВрддрд┐рдо рдЙрдкрдпреЛрдЧрдХрд░реНрддрд╛ рдмреИрдХрдЧреНрд░рд╛рдЙрдВрдб рдореЗрдВ рдкреНрд░реЛрд╕реЗрд╕рд┐рдВрдЧ рдХрд░рддреЗ рд╕рдордп рдХреНрд▓рд╛рдЗрдВрдЯ рдХреА рдУрд░ рд╕реЗ рдЕрдиреНрдп рдХрд╛рдо рдХрд░ рд╕рдХреЗрдЧрд╛ред рдЗрд╕ рдорд╛рдорд▓реЗ рдореЗрдВ, рдЖрдкрдХрд╛ рдПрдкреНрд▓рд┐рдХреЗрд╢рди рдЕрдиреНрдп рдЙрдкрдпреЛрдЧрдХрд░реНрддрд╛рдУрдВ рдФрд░ рдХреНрд▓рд╛рдЗрдВрдЯ рд╕реЗ рдЕрдиреБрд░реЛрдзреЛрдВ рдХрд╛ рд╕реНрд╡рддрдВрддреНрд░ рд░реВрдк рд╕реЗ рдЬрд╡рд╛рдм рджреЗрдиреЗ рдореЗрдВ рднреА рд╕рдХреНрд╖рдо рд╣реЛрдЧрд╛ред

рдЖрдЬ рд╣рдо рдРрд╕реА рд╕рдорд╕реНрдпрд╛рдУрдВ рдХреЗ рд╕рдорд╛рдзрд╛рди рдХреЗ рд▓рд┐рдП Django рдПрдкреНрд▓рд┐рдХреЗрд╢рди рдореЗрдВ рд▓рдВрдмреЗ рд╕рдордп рд╕реЗ рдЪрд▓ рд░рд╣реА рдкреНрд░рдХреНрд░рд┐рдпрд╛рдУрдВ рдХреЛ рд╕рдВрднрд╛рд▓рдиреЗ рдХреЗ рд▓рд┐рдП рд╕реЗрд▓реЗрд░реА рдФрд░ рд░реЗрдбрд┐рд╕ рдХреЛ рд╕реНрдерд╛рдкрд┐рдд рдХрд░рдиреЗ рдФрд░ рдХреЙрдиреНрдлрд╝рд┐рдЧрд░ рдХрд░рдиреЗ рдХреА рдкреНрд░рдХреНрд░рд┐рдпрд╛ рдХреЗ рдмрд╛рд░реЗ рдореЗрдВ рдмрд╛рдд рдХрд░реЗрдВрдЧреЗ ред рд╣рдо рд╕рднреА рдЯреБрдХрдбрд╝реЛрдВ рдХреЛ рдПрдХ рд╕рд╛рде рдмрд╛рдБрдзрдиреЗ рдХреЗ рд▓рд┐рдП рдбреЙрдХрд░ рдФрд░ рдбреЙрдХрд░ рдХрдВрдкреЛрдЬрд╝ рдХрд╛ рднреА рдЙрдкрдпреЛрдЧ рдХрд░реЗрдВрдЧреЗ рдФрд░ рджреЗрдЦреЗрдВрдЧреЗ рдХрд┐ рдпреВрдирд┐рдЯ рдФрд░ рдЗрдВрдЯреАрдЧреНрд░реЗрд╢рди рдЯреЗрд╕реНрдЯ рдХреЗ рд╕рд╛рде рд╕реЗрд▓реЗрд░реА рдХреА рдиреМрдХрд░рд┐рдпреЛрдВ рдХрд╛ рдкрд░реАрдХреНрд╖рдг рдХреИрд╕реЗ рдХрд┐рдпрд╛ рдЬрд╛рдПред

рдЗрд╕ рдЧрд╛рдЗрдб рдХреЗ рдЕрдВрдд рддрдХ, рд╣рдо рд╕реАрдЦреЗрдВрдЧреЗ:

  • рдкреГрд╖реНрдарднреВрдорд┐ рдХреА рдиреМрдХрд░рд┐рдпреЛрдВ рдХрд╛ рдирд┐рд░реНрдорд╛рдг рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП Django рдореЗрдВ рдЕрдЬрд╡рд╛рдЗрди рдХреЛ рдПрдХреАрдХреГрдд рдХрд░реЗрдВред
  • рдбреЙрдХрд┐рдВрдЧ рдХреЗ рд╕рд╛рде рдкреИрдХ Django, рдЕрдЬрд╡рд╛рдЗрди, рдФрд░ рд░реЗрдбрд┐рд╕ред
  • рдЕрд▓рдЧ рд╡рд░реНрдХрдлрд╝реНрд▓реЛ рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░рдХреЗ рдкреГрд╖реНрдарднреВрдорд┐ рдореЗрдВ рдкреНрд░рдХреНрд░рд┐рдпрд╛рдПрдБ рдЪрд▓рд╛рдПрдБред
  • рдХрд┐рд╕реА рдлрд╝рд╛рдЗрд▓ рдореЗрдВ рд╕реЗрд▓реЗрд░реА рд▓реЙрдЧ рд╕рд╣реЗрдЬреЗрдВред
  • рдЕрдЬрд╡рд╛рдЗрди рдХреА рдиреМрдХрд░рд┐рдпреЛрдВ рдФрд░ рд╢реНрд░рдорд┐рдХреЛрдВ рдХреА рдирд┐рдЧрд░рд╛рдиреА рдФрд░ рдкреНрд░рд╢рд╛рд╕рди рдХреЗ рд▓рд┐рдП рдлреВрд▓ рд╕реЗрдЯ рдХрд░реЗрдВ ред
  • рдЗрдХрд╛рдИ рдФрд░ рдПрдХреАрдХрд░рдг рдкрд░реАрдХреНрд╖рдгреЛрдВ рдХреЗ рд╕рд╛рде рдЯреЗрд╕реНрдЯ рд╕реЗрд▓реЗрд░реА рдЬреЙрдмреНрд╕ред

рдиреЗрдкрдереНрдп рдХрд╛рд░реНрдп


рдЙрдкрдпреЛрдЧрдХрд░реНрддрд╛ рдЕрдиреБрднрд╡ рдХреЛ рдмреЗрд╣рддрд░ рдмрдирд╛рдиреЗ рдХреЗ рд▓рд┐рдП, рд▓рдВрдмреА рдкреНрд░рдХреНрд░рд┐рдпрд╛рдУрдВ рдХреЛ рд╕рд╛рдорд╛рдиреНрдп HTTP рдЕрдиреБрд░реЛрдз / рдкреНрд░рддрд┐рдХреНрд░рд┐рдпрд╛ рд╕реНрдЯреНрд░реАрдо рдХреЗ рдмрд╛рд╣рд░ рдкреГрд╖реНрдарднреВрдорд┐ рдореЗрдВ рдЪрд▓рдирд╛ рдЪрд╛рд╣рд┐рдПред

рдЙрджрд╛рд╣рд░рдг рдХреЗ рд▓рд┐рдП:

  • рдкреБрд╖реНрдЯрд┐ рдХреЗ рд▓рд┐рдП рдкрддреНрд░ рднреЗрдЬрдирд╛;
  • рд╡реЗрдм рд╕реНрдХреИрдкрд┐рдВрдЧ рдФрд░ рдХреНрд░реЙрд▓рд┐рдВрдЧ;
  • рдбреЗрдЯрд╛ рд╡рд┐рд╢реНрд▓реЗрд╖рдг;
  • рдЗрдореЗрдЬ рдкреНрд░реЛрд╕реЗрд╕рд┐рдВрдЧ;
  • рд░рд┐рдкреЛрд░реНрдЯ рдкреАрдврд╝реАред

рдПрдкреНрд▓рд┐рдХреЗрд╢рди рдмрдирд╛рддреЗ рд╕рдордп, рдЙрди рдХрд╛рд░реНрдпреЛрдВ рдХреЛ рдЕрд▓рдЧ рдХрд░рдиреЗ рдХрд╛ рдкреНрд░рдпрд╛рд╕ рдХрд░реЗрдВ, рдЬрд┐рдиреНрд╣реЗрдВ рдЕрдиреБрд░реЛрдз / рдкреНрд░рддрд┐рдХреНрд░рд┐рдпрд╛ рдХреЗ рдЬреАрд╡рди рдХреЗ рджреМрд░рд╛рди рдХрд┐рдпрд╛ рдЬрд╛рдирд╛ рдЪрд╛рд╣рд┐рдП, рдЙрджрд╛рд╣рд░рдг рдХреЗ рд▓рд┐рдП, рд╕реАрдЖрд░рдпреВрдбреА рд╕рдВрдЪрд╛рд▓рди, рдЙрди рдХрд╛рд░реНрдпреЛрдВ рд╕реЗ рдЬрд┐рдиреНрд╣реЗрдВ рдкреГрд╖реНрдарднреВрдорд┐ рдореЗрдВ рдХрд┐рдпрд╛ рдЬрд╛рдирд╛ рдЪрд╛рд╣рд┐рдПред

рдХрд╛рдо рдХрд░рдиреЗ рдХреА рдкреНрд░рдХреНрд░рд┐рдпрд╛


рд╣рдорд╛рд░рд╛ рд▓рдХреНрд╖реНрдп рдПрдХ Django рдПрдкреНрд▓рд┐рдХреЗрд╢рди рд╡рд┐рдХрд╕рд┐рдд рдХрд░рдирд╛ рд╣реИ рдЬреЛ рдЕрдиреБрд░реЛрдз / рдкреНрд░рддрд┐рдХреНрд░рд┐рдпрд╛ рдЪрдХреНрд░ рдХреЗ рдмрд╛рд╣рд░ рд▓рдВрдмреА рдкреНрд░рдХреНрд░рд┐рдпрд╛рдУрдВ рдХреЛ рд╕рдВрднрд╛рд▓рдиреЗ рдХреЗ рд▓рд┐рдП рдЕрдЬрд╡рд╛рдЗрди рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░рддрд╛ рд╣реИред

  1. рдЕрдВрддрд┐рдо рдЙрдкрдпреЛрдЧрдХрд░реНрддрд╛ рд╕рд░реНрд╡рд░ рдкрд░ рдПрдХ POST рдЕрдиреБрд░реЛрдз рднреЗрдЬрдХрд░ рдПрдХ рдирдИ рдиреМрдХрд░реА рдЙрддреНрдкрдиреНрди рдХрд░рддрд╛ рд╣реИред
  2. рдЗрд╕ рджреГрд╢реНрдп рдореЗрдВ, рдиреМрдХрд░реА рдХреЛ рдХрддрд╛рд░ рдореЗрдВ рдЬреЛрдбрд╝рд╛ рдЬрд╛рддрд╛ рд╣реИ, рдФрд░ рдиреМрдХрд░реА рдЖрдИрдбреА рдХреНрд▓рд╛рдЗрдВрдЯ рдХреЛ рд╡рд╛рдкрд╕ рднреЗрдЬ рджреА рдЬрд╛рддреА рд╣реИред
  3. AJAX рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░рддреЗ рд╣реБрдП, рдХреНрд▓рд╛рдЗрдВрдЯ рдиреМрдХрд░реА рдХреА рд╕реНрдерд┐рддрд┐ рдХреА рдЬрд╛рдВрдЪ рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП рд╕рд░реНрд╡рд░ рдХреЛ рдХреНрд╡реЗрд░реА рдХрд░рдирд╛ рдЬрд╛рд░реА рд░рдЦрддрд╛ рд╣реИ, рдЬрдмрдХрд┐ рдиреМрдХрд░реА рд╕реНрд╡рдпрдВ рдкреГрд╖реНрдарднреВрдорд┐ рдореЗрдВ рдЪрд▓ рд░рд╣реА рд╣реИред



рдкрд░рд┐рдпреЛрдЬрдирд╛ рдирд┐рд░реНрдорд╛рдг


Django-celery рд░рд┐рдкреЙрдЬрд┐рдЯрд░реА рд╕реЗ рдкреНрд░реЛрдЬреЗрдХреНрдЯ рдХреЛ рдХреНрд▓реЛрди рдХрд░реЗрдВ рдФрд░ рдорд╛рд╕реНрдЯрд░ рдмреНрд░рд╛рдВрдЪ рдореЗрдВ v1 рдЯреИрдЧ рдкрд░ рдПрдХ рдЪреЗрдХрдЖрдЙрдЯ рдХрд░реЗрдВ :

$ git clone https://github.com/testdrivenio/django-celery --branch v1 --single-branch
$ cd django-celery
$ git checkout v1 -b master

рдЪреВрдВрдХрд┐ рдХреБрд▓ рдорд┐рд▓рд╛рдХрд░ рд╣рдореЗрдВ рддреАрди рдкреНрд░рдХреНрд░рд┐рдпрд╛рдУрдВ (Django, Redis, Worker) рдХреЗ рд╕рд╛рде рдХрд╛рдо рдХрд░рдиреЗ рдХреА рдЖрд╡рд╢реНрдпрдХрддрд╛ рд╣реИ, рдЗрд╕рд▓рд┐рдП рд╣рдо рдЙрдиреНрд╣реЗрдВ рдХрдиреЗрдХреНрдЯ рдХрд░рдХреЗ рдХрд╛рд░реНрдп рдХреЛ рд╕рд░рд▓ рдмрдирд╛рдиреЗ рдХреЗ рд▓рд┐рдП Docker рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░рддреЗ рд╣реИрдВ рддрд╛рдХрд┐ рд╣рдо рдПрдХ рдЯрд░реНрдорд┐рдирд▓ рд╡рд┐рдВрдбреЛ рдореЗрдВ рдПрдХ рдХрдорд╛рдВрдб рдХреЗ рд╕рд╛рде рд╕рдм рдХреБрдЫ рдЪрд▓рд╛ рд╕рдХреЗрдВред

рдкреНрд░реЛрдЬреЗрдХреНрдЯ рд░реВрдЯ рд╕реЗ, рдЪрд┐рддреНрд░ рдмрдирд╛рдПрдВ рдФрд░ рдбреЙрдХрдЯрд░ рдХрдВрдЯреЗрдирд░ рд▓реЙрдиреНрдЪ рдХрд░реЗрдВ:

$ docker-compose up -d --build

рдЬрдм рдирд┐рд░реНрдорд╛рдг рдкреВрд░рд╛ рд╣реЛ рдЬрд╛рддрд╛ рд╣реИ, рддреЛ рд▓реЛрдХрд▓рд╣реЛрд╕реНрдЯ рдкрд░ рдЬрд╛рдПрдВ : 1337:



рд╕рддреНрдпрд╛рдкрд┐рдд рдХрд░реЗрдВ рдХрд┐ рдкрд░реАрдХреНрд╖рдг рд╕рдлрд▓рддрд╛рдкреВрд░реНрд╡рдХ рдкрд╛рд╕ рд╣реБрдП рд╣реИрдВ:

$ docker-compose exec web python -m pytest

======================================== test session starts ========================================
platform linux -- Python 3.8.2, pytest-5.4.1, py-1.8.1, pluggy-0.13.1
django: settings: core.settings (from ini)
rootdir: /usr/src/app, inifile: pytest.ini
plugins: django-3.8.0
collected 1 item

tests/test_tasks.py .                                                                         [100%]

========================================= 1 passed in 0.47s =========================================

рдЖрдЧреЗ рдмрдврд╝рдиреЗ рд╕реЗ рдкрд╣рд▓реЗ рдкреНрд░реЛрдЬреЗрдХреНрдЯ рдХреА рд╕рдВрд░рдЪрдирд╛ рдкрд░ рдПрдХ рдирдЬрд╝рд░ рдбрд╛рд▓рддреЗ рд╣реИрдВ:

тФЬтФАтФА .gitignore
тФЬтФАтФА LICENSE
тФЬтФАтФА README.md
тФЬтФАтФА docker-compose.yml
тФФтФАтФА project
    тФЬтФАтФА Dockerfile
    тФЬтФАтФА core
    тФВ   тФЬтФАтФА __init__.py
    тФВ   тФЬтФАтФА asgi.py
    тФВ   тФЬтФАтФА settings.py
    тФВ   тФЬтФАтФА urls.py
    тФВ   тФФтФАтФА wsgi.py
    тФЬтФАтФА entrypoint.sh
    тФЬтФАтФА manage.py
    тФЬтФАтФА pytest.ini
    тФЬтФАтФА requirements.txt
    тФЬтФАтФА static
    тФВ   тФЬтФАтФА bulma.min.css
    тФВ   тФЬтФАтФА jquery-3.4.1.min.js
    тФВ   тФЬтФАтФА main.css
    тФВ   тФФтФАтФА main.js
    тФЬтФАтФА tasks
    тФВ   тФЬтФАтФА __init__.py
    тФВ   тФЬтФАтФА apps.py
    тФВ   тФЬтФАтФА migrations
    тФВ   тФВ   тФФтФАтФА __init__.py
    тФВ   тФЬтФАтФА templates
    тФВ   тФВ   тФФтФАтФА home.html
    тФВ   тФФтФАтФА views.py
    тФФтФАтФА tests
        тФЬтФАтФА __init__.py
        тФФтФАтФА test_tasks.py

рдиреМрдХрд░реА рдХрд╛ рд╢реБрднрд╛рд░рдВрдн


рдИрд╡реЗрдВрдЯ рд╣реИрдВрдбрд▓рд░ рдХреЛ project/static/main.jsрдПрдХ рдмрдЯрди рдХреЗ рдХреНрд▓рд┐рдХ рдкрд░ рд╕рдмреНрд╕рдХреНрд░рд╛рдЗрдм рдХрд┐рдпрд╛ рдЬрд╛рддрд╛ рд╣реИред рд╕рд░реНрд╡рд░ рдкрд░ рдХреНрд▓рд┐рдХ рдХрд░рддреЗ рд╣реБрдП рдЙрдЪрд┐рдд рдиреМрдХрд░реА рдкреНрд░рдХрд╛рд░ рдХреЗ рд╕рд╛рде рдПрдХ AJAX рдХреЗ рдмрд╛рдж рдЕрдиреБрд░реЛрдз рднреЗрдЬрддрд╛ рд╣реИ: 1, 2рдпрд╛ 3ред

$('.button').on('click', function() {
  $.ajax({
    url: '/tasks/',
    data: { type: $(this).data('type') },
    method: 'POST',
  })
  .done((res) => {
    getStatus(res.task_id);
  })
  .fail((err) => {
    console.log(err);
  });
});

рд╕рд░реНрд╡рд░ рд╕рд╛рдЗрдб рдкрд░, рдЕрдиреБрд░реЛрдз рдХреЛ рд╕рдВрд╕рд╛рдзрд┐рдд рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП рдПрдХ рджреГрд╢реНрдп рдкрд╣рд▓реЗ рд╣реА рдХреЙрдиреНрдлрд╝рд┐рдЧрд░ рдХрд┐рдпрд╛ рдЧрдпрд╛ рд╣реИ project/tasks/views.py:

def run_task(request):
    if request.POST:
        task_type = request.POST.get("type")
        return JsonResponse({"task_type": task_type}, status=202)

рдФрд░ рдЕрдм рдордЬрд╝рд╛ рд╢реБрд░реВ рд╣реЛрддрд╛ рд╣реИ: рд╣рдо рдЕрдЬрд╡рд╛рдЗрди рдЯрд╛рдИ!

рдЕрдЬрд╡рд╛рдЗрди рд╕реЗрдЯрдЕрдк


рдЖрдЗрдП рдлрд╛рдЗрд▓ рдореЗрдВ рд╕реЗрд▓реЗрд░реА рдФрд░ рд░реЗрдбрд┐рд╕ рдЬреЛрдбрд╝рдХрд░ рд╢реБрд░реВ рдХрд░реЗрдВ project/requirements.txt:

celery==4.4.1
Django==3.0.4
redis==3.4.1

pytest==5.4.1
pytest-django==3.8.0

рдЕрдЬрд╡рд╛рдЗрди рдПрдХ рд╕рдВрджреЗрд╢ рдмреНрд░реЛрдХрд░ рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░рддрд╛ рд╣реИ - RabbitMQ , Redis, рдпрд╛ AWS Simple Queue Service (SQS) тАФрд╕реЗрд▓реЗрд░реА рдХрд╛рд░реНрдпрдХрд░реНрддрд╛ рдФрд░ рд╡реЗрдм рдПрдкреНрд▓рд┐рдХреЗрд╢рди рдХреЗ рдмреАрдЪ рд╕рдВрдЪрд╛рд░ рдХреЛ рд╕рд░рд▓ рдХрд░рддрд╛ рд╣реИред рд╕рдВрджреЗрд╢ рджрд▓рд╛рд▓ рдХреЛ рднреЗрдЬреЗ рдЬрд╛рддреЗ рд╣реИрдВ, рдФрд░ рдлрд┐рд░ рдХрд╛рд░реНрдпрдХрд░реНрддрд╛ рджреНрд╡рд╛рд░рд╛ рд╕рдВрд╕рд╛рдзрд┐рдд рдХрд┐рдпрд╛ рдЬрд╛рддрд╛ рд╣реИред рдЙрд╕рдХреЗ рдмрд╛рдж, рдкрд░рд┐рдгрд╛рдо рдмреИрдХрдПрдВрдб рдкрд░ рднреЗрдЬреЗ рдЬрд╛рддреЗ рд╣реИрдВред

рд░реЗрдбрд┐рд╕ рдмреНрд░реЛрдХрд░ рдФрд░ рдмреИрдХрдПрдВрдб рджреЛрдиреЛрдВ рд╣реЛрдВрдЧреЗред docker-compose.ymlрдирд┐рдореНрдирд╛рдиреБрд╕рд╛рд░ рдлрд╝рд╛рдЗрд▓ рдореЗрдВ Redis рдФрд░ рдЕрдЬрд╡рд╛рдЗрди рдХрд╛рд░реНрдпрдХрд░реНрддрд╛ рдЬреЛрдбрд╝реЗрдВ :

version: '3.7'

services:
  web:
    build: ./project
    command: python manage.py runserver 0.0.0.0:8000
    volumes:
      - ./project:/usr/src/app/
    ports:
      - 1337:8000
    environment:
      - DEBUG=1
      - SECRET_KEY=dbaa1_i7%*3r9-=z-+_mz4r-!qeed@(-a_r(g@k8jo8y3r27%m
      - DJANGO_ALLOWED_HOSTS=localhost 127.0.0.1 [::1]
      - CELERY_BROKER=redis://redis:6379/0
      - CELERY_BACKEND=redis://redis:6379/0
    depends_on:
      - redis

  celery:
    build: ./project
    command: celery worker --app=core --loglevel=info
    volumes:
      - ./project:/usr/src/app
    environment:
      - DEBUG=1
      - SECRET_KEY=dbaa1_i7%*3r9-=z-+_mz4r-!qeed@(-a_r(g@k8jo8y3r27%m
      - DJANGO_ALLOWED_HOSTS=localhost 127.0.0.1 [::1]
      - CELERY_BROKER=redis://redis:6379/0
      - CELERY_BACKEND=redis://redis:6379/0
    depends_on:
      - web
      - redis

  redis:
    image: redis:5-alpine

рдЗрд╕ рдкрд░ рдзреНрдпрд╛рди рджреЗрдВ celery worker --app=core --loglevel=info:

  1. celery workerрдЕрдЬрд╡рд╛рдЗрди рдХрд╛рд░реНрдпрдХрд░реНрддрд╛ рд╢реБрд░реВ рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП рдЗрд╕реНрддреЗрдорд╛рд▓ рдХрд┐рдпрд╛ ;
  2. --app=coreрд╕реЗрд▓реЗрд░реА core рдПрдкреНрд▓рд┐рдХреЗрд╢рди рд▓реЙрдиреНрдЪ рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП рдЙрдкрдпреЛрдЧ рдХрд┐рдпрд╛ рдЬрд╛рддрд╛ рд╣реИ (рдЬрд┐рд╕реЗ рд╣рдо рд╢реАрдШреНрд░ рд╣реА рдкрд░рд┐рднрд╛рд╖рд┐рдд рдХрд░реЗрдВрдЧреЗ);
  3. --loglevel=infoрд╕реВрдЪрдирд╛ рд▓реЙрдЧрд┐рдВрдЧ рдХрд╛ рд╕реНрддрд░ рдирд┐рд░реНрдзрд╛рд░рд┐рдд рдХрд░рддрд╛ рд╣реИ ред

рдирд┐рдореНрдирд▓рд┐рдЦрд┐рдд рдкреНрд░реЛрдЬреЗрдХреНрдЯ рд╕реЗрдЯрд┐рдВрдЧреНрд╕ рдореЙрдбреНрдпреВрд▓ рдореЗрдВ рдЬреЛрдбрд╝реЗрдВ рддрд╛рдХрд┐ рд╕реЗрд▓реЗрд░реА рдмреНрд░реЛрдХрд░ рдФрд░ рдмреИрдХрдПрдВрдб рдХреЗ рд░реВрдк рдореЗрдВ рд░реЗрдбрд┐рд╕ рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░реЗ:

CELERY_BROKER_URL = os.environ.get("CELERY_BROKER", "redis://redis:6379/0")
CELERY_RESULT_BACKEND = os.environ.get("CELERY_BROKER", "redis://redis:6379/0")

рдлрд┐рд░ рдлрд╝рд╛рдЗрд▓ рдмрдирд╛рдиреЗ sample_tasks.pyрдореЗрдВ project/tasks: рдпрд╣рд╛рдБ, рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░ shared_task рдбреЗрдХреЛрд░реЗрдЯрд░ , рд╣рдо рдПрдХ рдирдП рдЕрдЬрд╡рд╛рдЗрди рдХрд╛рд░реНрдп рдмреБрд▓рд╛рдпрд╛ рд╕рдорд╛рд░реЛрд╣ рдореЗрдВ рдкрд░рд┐рднрд╛рд╖рд┐рдд рдХрд┐рдпрд╛ ред рдпрд╛рдж рд░рдЦреЗрдВ рдХрд┐ рдХрд╛рд░реНрдп рд╕реНрд╡рдпрдВ Django рдкреНрд░рдХреНрд░рд┐рдпрд╛ рд╕реЗ рдирд┐рд╖реНрдкрд╛рджрд┐рдд рдирд╣реАрдВ рдХрд┐рдпрд╛ рдЬрд╛рдПрдЧрд╛, рдпрд╣ Celery рдХрд╛рд░реНрдпрдХрд░реНрддрд╛ рджреНрд╡рд╛рд░рд╛ рдХрд┐рдпрд╛ рдЬрд╛рдПрдЧрд╛ред рдЕрдм рдлрд╝рд╛рдЗрд▓ рдЬреЛрдбрд╝рдиреЗ рдХреЗ рд▓рд┐рдП :

# project/tasks/sample_tasks.py

import time

from celery import shared_task

@shared_task
def create_task(task_type):
time.sleep(int(task_type) * 10)
return True


create_task



celery.py"project/core"

import os

from celery import Celery


os.environ.setdefault("DJANGO_SETTINGS_MODULE", "core.settings")
app = Celery("core")
app.config_from_object("django.conf:settings", namespace="CELERY")
app.autodiscover_tasks()

рдпрд╣рд╛рдБ рдХреНрдпрд╛ рдЪрд▓ рд░рд╣рд╛ рд╣реИ?

  1. рдкрд╣рд▓реЗ рдЖрдкрдХреЛ рдкрд░реНрдпрд╛рд╡рд░рдг рдХреЗ рд▓рд┐рдП рдбрд┐рдлрд╝реЙрд▓реНрдЯ рдорд╛рди рд╕реЗрдЯ рдХрд░рдиреЗ рдХреА рдЖрд╡рд╢реНрдпрдХрддрд╛ рд╣реИ DJANGO_SETTINGS_MODULEрддрд╛рдХрд┐ рд╕реЗрд▓реЗрд░реА рдХреЛ рдкрддрд╛ рдЪрд▓реЗ рдХрд┐ рдХреИрд╕реЗ Django рдкреНрд░реЛрдЬреЗрдХреНрдЯ рдХреЛ рдЦреЛрдЬрдирд╛ рд╣реИред
  2. рдлрд┐рд░ рд╣рдордиреЗ рдПрдХ рдирд╛рдо рдХреЗ рд╕рд╛рде рдЕрдЬрд╡рд╛рдЗрди рдХрд╛ рдПрдХ рдЙрджрд╛рд╣рд░рдг рдмрдирд╛рдпрд╛ coreрдФрд░ рдЗрд╕реЗ рдПрдХ рдЪрд░ рдореЗрдВ рд░рдЦрд╛ appред
  3. рдлрд┐рд░ рд╣рдордиреЗ рд╕реЗрдЯрд┐рдВрдЧреНрд╕ рдСрдмреНрдЬреЗрдХреНрдЯ рд╕реЗ рд╕реЗрд▓реЗрд░реА рдХреЙрдиреНрдлрд╝рд┐рдЧрд░реЗрд╢рди рдорд╛рди рд▓реЛрдб рдХрд┐рдП django.confред рд╣рдордиреЗ рдЕрдиреНрдп Django рд╕реЗрдЯрд┐рдВрдЧреНрд╕ рдХреЗ рд╕рд╛рде рд╕рдВрдШрд░реНрд╖ рдХреЛ рд░реЛрдХрдиреЗ рдХреЗ рд▓рд┐рдП рдирд╛рдорд╕реНрдерд╛рди = "CELERY" рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд┐рдпрд╛ ред рдЗрд╕рд▓рд┐рдП, рдЕрдЬрд╡рд╛рдЗрди рдХреЗ рд▓рд┐рдП рд╕рднреА рдХреЙрдиреНрдлрд╝рд┐рдЧрд░реЗрд╢рди рд╕реЗрдЯрд┐рдВрдЧреНрд╕ рдПрдХ рдЙрдкрд╕рд░реНрдЧ рдХреЗ рд╕рд╛рде рд╢реБрд░реВ рд╣реЛрдиреА рдЪрд╛рд╣рд┐рдП CELERY_ред
  4. рдЕрдВрдд рдореЗрдВ, app.autodiscover_tasks()рд╕реЗрд▓реЗрд░реА рдХреЛ рдмрддрд╛рддрд╛ рд╣реИ рдХрд┐ рдЗрд╕рдореЗрдВ рдкрд░рд┐рднрд╛рд╖рд┐рдд рдЕрдиреБрдкреНрд░рдпреЛрдЧреЛрдВ рд╕реЗ рдиреМрдХрд░рд┐рдпреЛрдВ рдХреА рддрд▓рд╛рд╢ рдХрд░реЗрдВ settings.INSTALLED_APPSред

рдмрджрд▓реЗрдВ project/core/__init__.pyрддрд╛рдХрд┐ рдЬрдм Django рд╢реБрд░реВ рд╣реЛ рддреЛ рдЕрдЬрд╡рд╛рдЗрди рдПрдкреНрд▓рд┐рдХреЗрд╢рди рдЕрдкрдиреЗ рдЖрдк рдЖрдпрд╛рдд рд╣реЛ рдЬрд╛рдП:

from .celery import app as celery_app


__all__ = ("celery_app",)

рдиреМрдХрд░реА рдХрд╛ рд╢реБрднрд╛рд░рдВрдн


рдиреМрдХрд░реА рд╢реБрд░реВ рдХрд░рдиреЗ рдФрд░ рдЖрдИрдбреА рднреЗрдЬрдиреЗ рдХреЗ рд▓рд┐рдП рджреГрд╢реНрдп рдХреЛ рддрд╛рдЬрд╝рд╛ рдХрд░реЗрдВ:

@csrf_exempt
def run_task(request):
    if request.POST:
        task_type = request.POST.get("type")
        task = create_task.delay(int(task_type))
        return JsonResponse({"task_id": task.id}, status=202)

рдХрд╛рд░реНрдп рдЖрдпрд╛рдд рдХрд░рдирд╛ рди рднреВрд▓реЗрдВ:

from tasks.sample_tasks import create_task

рдЫрд╡рд┐рдпрд╛рдВ рдПрдХрддреНрд░ рдХрд░реЗрдВ рдФрд░ рдирдП рдХрдВрдЯреЗрдирд░ рддреИрдирд╛рдд рдХрд░реЗрдВ:

$ docker-compose up -d --build

рдирдпрд╛ рдХрд╛рд░реНрдп рд╢реБрд░реВ рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП, рдХрд░реЗрдВ:

$ curl -F type=0 http://localhost:1337/tasks/

рдЖрдк рдХреБрдЫ рдЗрд╕ рддрд░рд╣ рджреЗрдЦреЗрдВрдЧреЗ:

{
  "task_id": "6f025ed9-09be-4cbb-be10-1dce919797de"
}

рдХрд╛рд░реНрдп рд╕реНрдерд┐рддрд┐


рдХреНрд▓рд╛рдЗрдВрдЯ рд╕рд╛рдЗрдб рдИрд╡реЗрдВрдЯ рд╣реИрдВрдбрд▓рд░ рдкрд░ рд▓реМрдЯреЗрдВ:

$('.button').on('click', function() {
  $.ajax({
    url: '/tasks/',
    data: { type: $(this).data('type') },
    method: 'POST',
  })
  .done((res) => {
    getStatus(res.task_id);
  })
  .fail((err) => {
    console.log(err);
  });
});

рдЬрдм AJAX рдЕрдиреБрд░реЛрдз рд╕реЗ рдкреНрд░рддрд┐рдХреНрд░рд┐рдпрд╛ рд╡рд╛рдкрд╕ рдЖрддреА рд╣реИ, рддреЛ рд╣рдо getStatus()рд╣рд░ рд╕реЗрдХрдВрдб рдиреМрдХрд░реА рдЖрдИрдбреА рд╕реЗ рднреЗрдЬреЗрдВрдЧреЗ :

function getStatus(taskID) {
  $.ajax({
    url: `/tasks/${taskID}/`,
    method: 'GET'
  })
  .done((res) => {
    const html = `
      <tr>
        <td>${res.task_id}</td>
        <td>${res.task_status}</td>
        <td>${res.task_result}</td>
      </tr>`
    $('#tasks').prepend(html);

    const taskStatus = res.task_status;

    if (taskStatus === 'SUCCESS' || taskStatus === 'FAILURE') return false;
    setTimeout(function() {
      getStatus(res.task_id);
    }, 1000);
  })
  .fail((err) => {
    console.log(err)
  });
}

рдпрджрд┐ рдЙрддреНрддрд░ рд╣рд╛рдБ рд╣реИ, рддреЛ DOM рдЯреЗрдмрд▓ рдореЗрдВ рдПрдХ рдирдИ рдкрдВрдХреНрддрд┐ рдЬреЛрдбрд╝реА рдЬрд╛рдПрдЧреАред get_statusрд╕реНрдерд┐рддрд┐ рд╡рд╛рдкрд╕ рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП рджреГрд╢реНрдп рдХреЛ рддрд╛рдЬрд╝рд╛ рдХрд░реЗрдВ :

@csrf_exempt
def get_status(request, task_id):
    task_result = AsyncResult(task_id)
    result = {
        "task_id": task_id,
        "task_status": task_result.status,
        "task_result": task_result.result
    }
    return JsonResponse(result, status=200)

рдЖрдпрд╛рдд AsyncResult :

from celery.result import AsyncResult

рдЕрджреНрдпрддрди рдХрдВрдЯреЗрдирд░:

$ docker-compose up -d --build

рдирдпрд╛ рдХрд╛рд░реНрдп рдЪрд▓рд╛рдПрдБ:

$ curl -F type=1 http://localhost:1337/tasks/

рдлрд┐рд░ task_idрдкреНрд░рддрд┐рдХреНрд░рд┐рдпрд╛ рд╕реЗ рдирд┐рдХрд╛рд▓реЗрдВ рдФрд░ get_statusрд╕реНрдерд┐рддрд┐ рдХреЛ рджреЗрдЦрдиреЗ рдХреЗ рд▓рд┐рдП рдЕрджреНрдпрддрди рдХреЙрд▓ рдХрд░реЗрдВ :

$ curl http://localhost:1337/tasks/25278457-0957-4b0b-b1da-2600525f812f/

{
    "task_id": "25278457-0957-4b0b-b1da-2600525f812f",
    "task_status": "SUCCESS",
    "task_result": true
}

рдЖрдк рдмреНрд░рд╛рдЙрдЬрд╝рд░ рдореЗрдВ рдПрдХ рд╣реА рдЬрд╛рдирдХрд╛рд░реА рджреЗрдЦ рд╕рдХрддреЗ рд╣реИрдВ:



рдЕрдЬрд╡рд╛рдЗрди рд▓реЙрдЧ


рдЗрд╕ рддрд░рд╣ рд╕реЗ рд╕реЗрд╡рд╛ celeryрдХреЛ рдЕрдкрдбреЗрдЯ рдХрд░реЗрдВ docker-compose.ymlрдХрд┐ рд╕реЗрд▓реЗрд░реА рдПрдХ рдЕрд▓рдЧ рдлрд╛рдЗрд▓ рдореЗрдВ рдмрдВрдж рд╣реЛ рдЬрд╛рдП:

celery:
  build: ./project
  command: celery worker --app=core --loglevel=info --logfile=logs/celery.log
  volumes:
    - ./project:/usr/src/app
  environment:
    - DEBUG=1
    - SECRET_KEY=dbaa1_i7%*3r9-=z-+_mz4r-!qeed@(-a_r(g@k8jo8y3r27%m
    - DJANGO_ALLOWED_HOSTS=localhost 127.0.0.1 [::1]
    - CELERY_BROKER=redis://redis:6379/0
    - CELERY_BACKEND=redis://redis:6379/0
  depends_on:
    - web
    - redis

"рдкреНрд░реЛрдЬреЗрдХреНрдЯ" рдХреЗ рд▓рд┐рдП рдПрдХ рдирдИ рдирд┐рд░реНрджреЗрд╢рд┐рдХрд╛ рдЬреЛрдбрд╝реЗрдВ рдФрд░ рдЗрд╕реЗ "рд▓реЙрдЧ" рдирд╛рдо рджреЗрдВ ред рдлрд┐рд░ рдЗрд╕ рдирдИ рдирд┐рд░реНрджреЗрд╢рд┐рдХрд╛ рдореЗрдВ рдлрд╝рд╛рдЗрд▓ рдЬреЛрдбрд╝реЗрдВ celery.logред

рдЕрдкрдбреЗрдЯ рдХрд░реЗрдВ:

$ docker-compose up -d --build

рдЖрдкрдХреЛ рдпрд╣ рджреЗрдЦрдирд╛ рдЪрд╛рд╣рд┐рдП рдХрд┐ рд╡реЙрд▓реНрдпреВрдо рд╕реЗрдЯ рдХрд░рдиреЗ рдХреЗ рдмрд╛рдж рд▓реЙрдЧ рдлрд╛рдЗрд▓ рд╕реНрдерд╛рдиреАрдп рд░реВрдк рд╕реЗ рдХреИрд╕реЗ рдкреЙрдкреБрд▓реЗрдЯ рд╣реЛрддреА рд╣реИ :

[2020-03-25 19:42:29,586: INFO/MainProcess] Connected to redis://redis:6379/0
[2020-03-25 19:42:29,599: INFO/MainProcess] mingle: searching for neighbors
[2020-03-25 19:42:30,635: INFO/MainProcess] mingle: all alone
[2020-03-25 19:42:30,664: WARNING/MainProcess]
    /usr/local/lib/python3.8/site-packages/celery/fixups/django.py:202:
    UserWarning: Using settings.DEBUG leads to a memory
    leak, never use this setting in production environments!
    warnings.warn('''Using settings.DEBUG leads to a memory
[2020-03-25 19:42:30,667: INFO/MainProcess] celery@6d060151bfeb ready.
[2020-03-25 19:43:07,103: INFO/MainProcess]
    Received task: tasks.sample_tasks.create_task[632792bb-5030-4f03-a0d8-e91979279729]
[2020-03-25 19:43:17,099: INFO/ForkPoolWorker-2]
    Task tasks.sample_tasks.create_task[632792bb-5030-4f03-a0d8-e91979279729]
    succeeded in 10.027462100006233s: True

рдлреНрд▓рд╛рд╡рд░ рдбреИрд╢рдмреЛрд░реНрдб


рдлреВрд▓ рд╡рд╛рд╕реНрддрд╡рд┐рдХ рд╕рдордп рдореЗрдВ рдЕрдЬрд╡рд╛рдЗрди рдХреА рдирд┐рдЧрд░рд╛рдиреА рдХреЗ рд▓рд┐рдП рдПрдХ рд╣рд▓реНрдХреЗ рд╡реЗрдм-рдЖрдзрд╛рд░рд┐рдд рдЙрдкрдХрд░рдг рд╣реИред рдЖрдк рдЙрджрд╛рд╣рд░рдг рдХреЗ рд▓рд┐рдП, рд░рдирд┐рдВрдЧ рдХрд╛рд░реНрдпреЛрдВ рдХреЛ рдЯреНрд░реИрдХ рдХрд░ рд╕рдХрддреЗ рд╣реИрдВ, рд╢реНрд░рдорд┐рдХреЛрдВ рдХреЗ рдкреВрд▓ рдХреЛ рдмрдврд╝рд╛ рдпрд╛ рдШрдЯрд╛ рд╕рдХрддреЗ рд╣реИрдВ, рдкреНрд░рджрд░реНрд╢рди рдЧреНрд░рд╛рдл рдФрд░ рдЖрдБрдХрдбрд╝реЗ рдкреНрд░рджрд░реНрд╢рд┐рдд рдХрд░ рд╕рдХрддреЗ рд╣реИрдВред

рдЗрд╕реЗ рдЗрд╕рдореЗрдВ рдЬреЛрдбрд╝реЗрдВ requirements.txt:

celery==4.4.1
Django==3.0.4
flower==0.9.3
redis==3.4.1

pytest==5.4.1
pytest-django==3.8.0

рдлрд┐рд░ рдЗрд╕рдореЗрдВ рдирдИ рд╕реЗрд╡рд╛ рдЬреЛрдбрд╝реЗрдВ docker-compose.yml:

dashboard:
  build: ./project
  command:  flower -A core --port=5555 --broker=redis://redis:6379/0
  ports:
    - 5555:5555
  environment:
    - DEBUG=1
    - SECRET_KEY=dbaa1_i7%*3r9-=z-+_mz4r-!qeed@(-a_r(g@k8jo8y3r27%m
    - DJANGO_ALLOWED_HOSTS=localhost 127.0.0.1 [::1]
    - CELERY_BROKER=redis://redis:6379/0
    - CELERY_BACKEND=redis://redis:6379/0
  depends_on:
    - web
    - redis
    - celery

рдФрд░ рдкрд░реАрдХреНрд╖рдг:

$ docker-compose up -d --build

рдбреИрд╢рдмреЛрд░реНрдб рджреЗрдЦрдиреЗ рдХреЗ рд▓рд┐рдП рд▓реЛрдХрд▓рд╣реЛрд╕реНрдЯ : 5555 рдкрд░ рдЬрд╛рдПрдВ ред рдЖрдкрдХреЛ рдПрдХ рдХрд╛рд░реНрдпрдХрд░реНрддрд╛ рдХреЛ рджреЗрдЦрдирд╛ рдЪрд╛рд╣рд┐рдП:



рдбреИрд╢рдмреЛрд░реНрдб рдХрд╛ рдкрд░реАрдХреНрд╖рдг рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП рдХреБрдЫ рдФрд░ рдХрд╛рд░реНрдп рдЪрд▓рд╛рдПрдВ:



рдЕрдзрд┐рдХ рд╢реНрд░рдорд┐рдХреЛрдВ рдХреЛ рдЬреЛрдбрд╝рдиреЗ рдХрд╛ рдкреНрд░рдпрд╛рд╕ рдХрд░реЗрдВ рдФрд░ рджреЗрдЦреЗрдВ рдХрд┐ рдпрд╣ рдкреНрд░рджрд░реНрд╢рди рдХреЛ рдХреИрд╕реЗ рдкреНрд░рднрд╛рд╡рд┐рдд рдХрд░рддрд╛ рд╣реИ:

$ docker-compose up -d --build --scale celery=3

рдЯреЗрд╕реНрдЯ


рдЪрд▓реЛ рд╕рдмрд╕реЗ рд╕рд░рд▓ рдкрд░реАрдХреНрд╖рдг рд╕реЗ рд╢реБрд░реВ рдХрд░рддреЗ рд╣реИрдВ:

def test_task():
    assert sample_tasks.create_task.run(1)
    assert sample_tasks.create_task.run(2)
    assert sample_tasks.create_task.run(3)

рдКрдкрд░ рджрд┐рдП рдЧрдП рдкрд░реАрдХреНрд╖рдг рдорд╛рдорд▓реЗ рдХреЛ project/tests/test_tasks.pyрдЬреЛрдбрд╝реЗрдВ рдФрд░ рдирд┐рдореНрди рдЖрдпрд╛рдд рдЬреЛрдбрд╝реЗрдВ:

from tasks import sample_tasks


рдпрд╣ рдкрд░реАрдХреНрд╖рдг рдЪрд▓рд╛рдПрдВ:

$ docker-compose exec web python -m pytest -k "test_task and not test_home"

рдЗрд╕ рдкрд░реАрдХреНрд╖рдг рдореЗрдВ рд▓рдЧрднрдЧ рдПрдХ рдорд┐рдирдЯ рд▓рдЧреЗрдЧрд╛:

======================================== test session starts ========================================
platform linux -- Python 3.8.2, pytest-5.4.1, py-1.8.1, pluggy-0.13.1
django: settings: core.settings (from ini)
rootdir: /usr/src/app, inifile: pytest.ini
plugins: django-3.8.0, celery-4.4.1
collected 2 items / 1 deselected / 1 selected

tests/test_tasks.py .                                                                         [100%]

============================ 1 passed, 1 deselected in 62.43s (0:01:02) =============================

рдпрд╣ рдзреНрдпрд╛рди рджреЗрдиреЗ рдпреЛрдЧреНрдп рд╣реИ рдХрд┐ рдКрдкрд░ рдХреЗ рдореБрдЦрд░ рдореЗрдВ рд╣рдордиреЗ рд╕реЗрд▓реЗрд░реА рдХрд╛рд░реНрдпрдХрд░реНрддрд╛ рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд┐рдП рдмрд┐рдирд╛, рд╕реАрдзреЗ рд▓реЙрдиреНрдЪ рдХрд╛рд░реНрдпреЛрдВ рдХреЗ .runрдмрдЬрд╛рдп рд╡рд┐рдзрд┐ .delayрдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд┐рдпрд╛ред рдЪреАрдЬреЛрдВ рдХреЛ рдЧрддрд┐
рджреЗрдиреЗ рдХреЗ рд▓рд┐рдП рдирдХрд▓реА рдкреНрд▓рдЧрдЗрдиреНрд╕ рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░рдирд╛ рдЪрд╛рд╣рддреЗ рд╣реИрдВ?

@patch('tasks.sample_tasks.create_task.run')
def test_mock_task(mock_run):
    assert sample_tasks.create_task.run(1)
    sample_tasks.create_task.run.assert_called_once_with(1)

    assert sample_tasks.create_task.run(2)
    assert sample_tasks.create_task.run.call_count == 2

    assert sample_tasks.create_task.run(3)
    assert sample_tasks.create_task.run.call_count == 3

рдЖрдпрд╛рдд:

from unittest.mock import patch, call

рдкрд░реАрдХреНрд╖рд╛:

$ docker-compose exec web python -m pytest -k "test_mock_task"

======================================== test session starts ========================================
platform linux -- Python 3.8.2, pytest-5.4.1, py-1.8.1, pluggy-0.13.1
django: settings: core.settings (from ini)
rootdir: /usr/src/app, inifile: pytest.ini
plugins: django-3.8.0, celery-4.4.1
collected 3 items / 2 deselected / 1 selected

tests/test_tasks.py .                                                                         [100%]

================================== 1 passed, 2 deselected in 1.13s ==================================

рджреЗрдЦ? рдЕрдм рдФрд░ рддреЗрдЬ!

рдкреВрд░реНрдг рдПрдХреАрдХрд░рдг рдкрд░реАрдХреНрд╖рдг рдХреЗ рдмрд╛рд░реЗ рдореЗрдВ рдХреНрдпрд╛?

def test_task_status(client):
    response = client.post(reverse("run_task"), {"type": 0})
    content = json.loads(response.content)
    task_id = content["task_id"]
    assert response.status_code == 202
    assert task_id

    response = client.get(reverse("get_status", args=[task_id]))
    content = json.loads(response.content)
    assert content == {"task_id": task_id, "task_status": "PENDING", "task_result": None}
    assert response.status_code == 200

    while content["task_status"] == "PENDING":
        response = client.get(reverse("get_status", args=[task_id]))
        content = json.loads(response.content)
    assert content == {"task_id": task_id, "task_status": "SUCCESS", "task_result": True}

рдпрд╛рдж рд░рдЦреЗрдВ рдХрд┐ рдпрд╣ рдкрд░реАрдХреНрд╖рдг рдЙрд╕реА рдмреНрд░реЛрдХрд░ рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░рддрд╛ рд╣реИ рдФрд░ рд╡рд┐рдХрд╛рд╕ рдореЗрдВ рдмреИрдХрдПрдВрдб рдХрд░рддрд╛ рд╣реИред рдЖрдк рдкрд░реАрдХреНрд╖рдг рдХреЗ рд▓рд┐рдП рдЕрдЬрд╡рд╛рдЗрди рдХрд╛ рдПрдХ рдирдпрд╛ рдЙрджрд╛рд╣рд░рдг рдмрдирд╛ рд╕рдХрддреЗ рд╣реИрдВ:

app = celery.Celery('tests', broker=CELERY_TEST_BROKER, backend=CELERY_TEST_BACKEND)

рдЖрдпрд╛рдд рдЬреЛрдбрд╝реЗрдВ:

import json

рдФрд░ рд╕реБрдирд┐рд╢реНрдЪрд┐рдд рдХрд░реЗрдВ рдХрд┐ рдкрд░реАрдХреНрд╖рдг рд╕рдлрд▓ рд╣реИрдВред

рдирд┐рд╖реНрдХрд░реНрд╖


рдЖрдЬ рд╣рдордиреЗ Django рдкрд░ рдПрдХ рдЖрд╡реЗрджрди рдореЗрдВ рджреАрд░реНрдШрдХрд╛рд▓рд┐рдХ рдХрд╛рд░реНрдпреЛрдВ рдХреЛ рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП рд╕реЗрд▓реЗрд░реА рдХреЗ рдореВрд▓ рд╡рд┐рдиреНрдпрд╛рд╕ рдХреА рд╢реБрд░реБрдЖрдд рдХреАред рдЖрдкрдХреЛ рдкреНрд░реЛрд╕реЗрд╕рд┐рдВрдЧ рдХрддрд╛рд░ рдореЗрдВ рдХреЛрдИ рднреА рдкреНрд░рдХреНрд░рд┐рдпрд╛ рднреЗрдЬрдиреА рдЪрд╛рд╣рд┐рдП рдЬреЛ рдЙрдкрдпреЛрдЧрдХрд░реНрддрд╛ рдХреА рдУрд░ рд╕реЗ рдХреЛрдб рдХреЛ рдзреАрдорд╛ рдХрд░ рд╕рдХрддреА рд╣реИред

рдЕрдЬрд╡рд╛рдЗрди рдХрд╛ рдЙрдкрдпреЛрдЧ рджреЛрд╣рд░рд╛рд╡ рд╡рд╛рд▓реЗ рдХрд╛рд░реНрдпреЛрдВ рдХреЛ рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП рдФрд░ рдЬрдЯрд┐рд▓ рд╕рдВрд╕рд╛рдзрди-рдЧрд╣рди рдХрд╛рд░реНрдпреЛрдВ рдХреЛ рд╡рд┐рдШрдЯрд┐рдд рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП рдХрд┐рдпрд╛ рдЬрд╛ рд╕рдХрддрд╛ рд╣реИ рддрд╛рдХрд┐ рдХрдИ рдорд╢реАрдиреЛрдВ рдкрд░ рдХрдореНрдкреНрдпреВрдЯреЗрд╢рдирд▓ рднрд╛рд░ рд╡рд┐рддрд░рд┐рдд рдХрд┐рдпрд╛ рдЬрд╛ рд╕рдХреЗ рдФрд░ рдирд┐рд╖реНрдкрд╛рджрди рдХреЗ рд╕рдордп рдФрд░ рдорд╢реАрди рдкрд░ рд▓реЛрдб рдХреЛ рдХрдо рдХрд┐рдпрд╛ рдЬрд╛ рд╕рдХреЗ рдЬреЛ рдХреНрд▓рд╛рдЗрдВрдЯ рдЕрдиреБрд░реЛрдзреЛрдВ рдХреЛ рд╕рдВрд╕рд╛рдзрд┐рдд рдХрд░рддрд╛ рд╣реИред

рдЖрдк рдЗрд╕ рд░рд┐рдкреЙрдЬрд┐рдЯрд░реА рдореЗрдВ рд╕рднреА рдХреЛрдб рдкрд╛ рд╕рдХрддреЗ рд╣реИрдВ ред



тЖТ рдХреЛрд░реНрд╕ рдкрд░ рдЬрд╛рдУ

All Articles