[{"data":1,"prerenderedAt":13459},["ShallowReactive",2],{"article_list_django_":3},[4,2878,5525,7191,10571],{"_path":5,"_dir":6,"_draft":7,"_partial":7,"_locale":8,"title":9,"description":10,"tags":11,"image":17,"excerpt":10,"publishDate":18,"body":19,"_type":2869,"_id":2870,"_source":2871,"_file":2872,"_stem":2873,"_extension":2874,"author":2875},"/jestep/2023-3/fastapi","2023-3",false,"","FastAPI: A High-Performance Python Framework for Rapid Web Development","FastAPI is a modern and high-performance Python web framework designed specifically for building APIs and web applications quickly and efficiently. Developed by Sebastián Ramírez and first released in 2018, FastAPI has rapidly gained traction in the developer community thanks to its focus on providing key features for API and web app development with excellent performance.",[12,13,14,15,16],"python","fastapi","django","flask","pyramid","/jestep/2023-3/img/header.png","2024-05-01",{"type":20,"children":21,"toc":2841},"root",[22,31,55,78,100,105,111,116,123,135,140,342,387,393,415,424,433,439,444,464,567,573,578,583,593,599,610,615,621,626,632,637,642,651,656,665,670,676,681,686,1166,1171,1345,1350,1593,1613,1619,1624,2236,2249,2255,2260,2525,2530,2536,2541,2547,2552,2558,2563,2569,2574,2580,2585,2591,2605,2611,2616,2622,2627,2633,2638,2694,2700,2705,2781,2786,2792,2797,2825,2830,2835],{"type":23,"tag":24,"props":25,"children":27},"element","h2",{"id":26},"introduction",[28],{"type":29,"value":30},"text","Introduction",{"type":23,"tag":32,"props":33,"children":34},"p",{},[35,44,46,53],{"type":23,"tag":36,"props":37,"children":41},"a",{"href":38,"rel":39},"https://fastapi.tiangolo.com/",[40],"nofollow",[42],{"type":29,"value":43},"FastAPI",{"type":29,"value":45}," is a modern and high-performance Python web framework designed specifically for building APIs and web applications quickly and efficiently. Developed by ",{"type":23,"tag":36,"props":47,"children":50},{"href":48,"rel":49},"https://tiangolo.com/",[40],[51],{"type":29,"value":52},"Sebastián Ramírez",{"type":29,"value":54}," and first released in 2018, FastAPI has rapidly gained traction in the developer community thanks to its focus on providing key features for API and web app development with excellent performance.",{"type":23,"tag":32,"props":56,"children":57},{},[58,60,67,69,76],{"type":29,"value":59},"FastAPI is built on top of ",{"type":23,"tag":36,"props":61,"children":64},{"href":62,"rel":63},"https://www.starlette.io/",[40],[65],{"type":29,"value":66},"Starlette",{"type":29,"value":68}," and ",{"type":23,"tag":36,"props":70,"children":73},{"href":71,"rel":72},"https://pydantic-docs.helpmanual.io/",[40],[74],{"type":29,"value":75},"Pydantic",{"type":29,"value":77},", both of which contribute to its ease of use and performance. Starlette provides a lightweight and fast ASGI framework foundation, while Pydantic enables data validation using Python type hints.",{"type":23,"tag":32,"props":79,"children":80},{},[81,83,90,91,98],{"type":29,"value":82},"Compared to well-established frameworks like ",{"type":23,"tag":36,"props":84,"children":87},{"href":85,"rel":86},"https://www.djangoproject.com/",[40],[88],{"type":29,"value":89},"Django",{"type":29,"value":68},{"type":23,"tag":36,"props":92,"children":95},{"href":93,"rel":94},"https://flask.palletsprojects.com/en/2.2.x/",[40],[96],{"type":29,"value":97},"Flask",{"type":29,"value":99},", FastAPI differentiates itself by emphasizing developer velocity, productivity, and maintainable code.",{"type":23,"tag":32,"props":101,"children":102},{},[103],{"type":29,"value":104},"In this post, we’ll look at FastAPI’s key features, compare it to alternatives like Django and Flask, and provide use cases where FastAPI shines. We’ll also share hands-on impressions of using the framework and offer suggestions on when FastAPI may be the right choice for your next project.",{"type":23,"tag":24,"props":106,"children":108},{"id":107},"fastapi-features-and-benefits",[109],{"type":29,"value":110},"FastAPI Features and Benefits",{"type":23,"tag":32,"props":112,"children":113},{},[114],{"type":29,"value":115},"FastAPI provides several features that make it well-suited for web development, especially APIs:",{"type":23,"tag":117,"props":118,"children":120},"h3",{"id":119},"automatic-data-validation",[121],{"type":29,"value":122},"Automatic Data Validation",{"type":23,"tag":32,"props":124,"children":125},{},[126,128,133],{"type":29,"value":127},"FastAPI leverages ",{"type":23,"tag":36,"props":129,"children":131},{"href":71,"rel":130},[40],[132],{"type":29,"value":75},{"type":29,"value":134}," and Python type hints to automatically validate data. This reduces boilerplate code and ensures that the API's inputs and outputs match the expected schemas.",{"type":23,"tag":32,"props":136,"children":137},{},[138],{"type":29,"value":139},"For example:",{"type":23,"tag":141,"props":142,"children":145},"pre",{"className":143,"code":144,"language":12,"meta":8,"style":8},"language-python shiki shiki-themes github-light github-dark","from pydantic import BaseModel\n\nclass User(BaseModel):\n    id: int\n    name: str\n    joined: date\n    \n@app.post(\"/users\")\nasync def create_user(user: User):\n    # user will be validated against the User model\n    return user\n",[146],{"type":23,"tag":147,"props":148,"children":149},"code",{"__ignoreMap":8},[150,178,188,218,238,252,261,270,294,318,328],{"type":23,"tag":151,"props":152,"children":155},"span",{"class":153,"line":154},"line",1,[156,162,168,173],{"type":23,"tag":151,"props":157,"children":159},{"style":158},"--shiki-default:#D73A49;--shiki-dark:#F97583",[160],{"type":29,"value":161},"from",{"type":23,"tag":151,"props":163,"children":165},{"style":164},"--shiki-default:#24292E;--shiki-dark:#E1E4E8",[166],{"type":29,"value":167}," pydantic ",{"type":23,"tag":151,"props":169,"children":170},{"style":158},[171],{"type":29,"value":172},"import",{"type":23,"tag":151,"props":174,"children":175},{"style":164},[176],{"type":29,"value":177}," BaseModel\n",{"type":23,"tag":151,"props":179,"children":181},{"class":153,"line":180},2,[182],{"type":23,"tag":151,"props":183,"children":185},{"emptyLinePlaceholder":184},true,[186],{"type":29,"value":187},"\n",{"type":23,"tag":151,"props":189,"children":191},{"class":153,"line":190},3,[192,197,203,208,213],{"type":23,"tag":151,"props":193,"children":194},{"style":158},[195],{"type":29,"value":196},"class",{"type":23,"tag":151,"props":198,"children":200},{"style":199},"--shiki-default:#6F42C1;--shiki-dark:#B392F0",[201],{"type":29,"value":202}," User",{"type":23,"tag":151,"props":204,"children":205},{"style":164},[206],{"type":29,"value":207},"(",{"type":23,"tag":151,"props":209,"children":210},{"style":199},[211],{"type":29,"value":212},"BaseModel",{"type":23,"tag":151,"props":214,"children":215},{"style":164},[216],{"type":29,"value":217},"):\n",{"type":23,"tag":151,"props":219,"children":221},{"class":153,"line":220},4,[222,228,233],{"type":23,"tag":151,"props":223,"children":225},{"style":224},"--shiki-default:#005CC5;--shiki-dark:#79B8FF",[226],{"type":29,"value":227},"    id",{"type":23,"tag":151,"props":229,"children":230},{"style":164},[231],{"type":29,"value":232},": ",{"type":23,"tag":151,"props":234,"children":235},{"style":224},[236],{"type":29,"value":237},"int\n",{"type":23,"tag":151,"props":239,"children":241},{"class":153,"line":240},5,[242,247],{"type":23,"tag":151,"props":243,"children":244},{"style":164},[245],{"type":29,"value":246},"    name: ",{"type":23,"tag":151,"props":248,"children":249},{"style":224},[250],{"type":29,"value":251},"str\n",{"type":23,"tag":151,"props":253,"children":255},{"class":153,"line":254},6,[256],{"type":23,"tag":151,"props":257,"children":258},{"style":164},[259],{"type":29,"value":260},"    joined: date\n",{"type":23,"tag":151,"props":262,"children":264},{"class":153,"line":263},7,[265],{"type":23,"tag":151,"props":266,"children":267},{"style":164},[268],{"type":29,"value":269},"    \n",{"type":23,"tag":151,"props":271,"children":273},{"class":153,"line":272},8,[274,279,283,289],{"type":23,"tag":151,"props":275,"children":276},{"style":199},[277],{"type":29,"value":278},"@app.post",{"type":23,"tag":151,"props":280,"children":281},{"style":164},[282],{"type":29,"value":207},{"type":23,"tag":151,"props":284,"children":286},{"style":285},"--shiki-default:#032F62;--shiki-dark:#9ECBFF",[287],{"type":29,"value":288},"\"/users\"",{"type":23,"tag":151,"props":290,"children":291},{"style":164},[292],{"type":29,"value":293},")\n",{"type":23,"tag":151,"props":295,"children":297},{"class":153,"line":296},9,[298,303,308,313],{"type":23,"tag":151,"props":299,"children":300},{"style":158},[301],{"type":29,"value":302},"async",{"type":23,"tag":151,"props":304,"children":305},{"style":158},[306],{"type":29,"value":307}," def",{"type":23,"tag":151,"props":309,"children":310},{"style":199},[311],{"type":29,"value":312}," create_user",{"type":23,"tag":151,"props":314,"children":315},{"style":164},[316],{"type":29,"value":317},"(user: User):\n",{"type":23,"tag":151,"props":319,"children":321},{"class":153,"line":320},10,[322],{"type":23,"tag":151,"props":323,"children":325},{"style":324},"--shiki-default:#6A737D;--shiki-dark:#6A737D",[326],{"type":29,"value":327},"    # user will be validated against the User model\n",{"type":23,"tag":151,"props":329,"children":331},{"class":153,"line":330},11,[332,337],{"type":23,"tag":151,"props":333,"children":334},{"style":158},[335],{"type":29,"value":336},"    return",{"type":23,"tag":151,"props":338,"children":339},{"style":164},[340],{"type":29,"value":341}," user\n",{"type":23,"tag":32,"props":343,"children":344},{},[345,347,353,355,361,363,369,371,377,379,385],{"type":29,"value":346},"Here, FastAPI automatically validates that the ",{"type":23,"tag":147,"props":348,"children":350},{"className":349},[],[351],{"type":29,"value":352},"user",{"type":29,"value":354}," object received matches the ",{"type":23,"tag":147,"props":356,"children":358},{"className":357},[],[359],{"type":29,"value":360},"User",{"type":29,"value":362}," model containing the expected types for ",{"type":23,"tag":147,"props":364,"children":366},{"className":365},[],[367],{"type":29,"value":368},"id",{"type":29,"value":370},", ",{"type":23,"tag":147,"props":372,"children":374},{"className":373},[],[375],{"type":29,"value":376},"name",{"type":29,"value":378},", and ",{"type":23,"tag":147,"props":380,"children":382},{"className":381},[],[383],{"type":29,"value":384},"joined",{"type":29,"value":386},".",{"type":23,"tag":117,"props":388,"children":390},{"id":389},"automatic-interactive-api-documentation",[391],{"type":29,"value":392},"Automatic Interactive API Documentation",{"type":23,"tag":32,"props":394,"children":395},{},[396,398,405,406,413],{"type":29,"value":397},"FastAPI integrates with ",{"type":23,"tag":36,"props":399,"children":402},{"href":400,"rel":401},"https://swagger.io/tools/swagger-ui/",[40],[403],{"type":29,"value":404},"Swagger UI",{"type":29,"value":68},{"type":23,"tag":36,"props":407,"children":410},{"href":408,"rel":409},"https://github.com/Redocly/redoc",[40],[411],{"type":29,"value":412},"ReDoc",{"type":29,"value":414}," to automatically generate interactive documentation for the API. This lets you quickly test endpoints and review parameters without additional work.",{"type":23,"tag":32,"props":416,"children":417},{},[418],{"type":23,"tag":419,"props":420,"children":423},"img",{"alt":421,"src":422},"Swagger UI docs example","/jestep/2023-3/img/index-03-swagger-02.png",[],{"type":23,"tag":32,"props":425,"children":426},{},[427],{"type":23,"tag":428,"props":429,"children":430},"em",{},[431],{"type":29,"value":432},"FastAPI Swagger UI interactive documentation",{"type":23,"tag":117,"props":434,"children":436},{"id":435},"modern-python-features",[437],{"type":29,"value":438},"Modern Python Features",{"type":23,"tag":32,"props":440,"children":441},{},[442],{"type":29,"value":443},"FastAPI takes advantage of modern Python features like type hints and asynchronous programming. This results in cleaner and more idiomatic code while improving performance.",{"type":23,"tag":32,"props":445,"children":446},{},[447,449,454,456,462],{"type":29,"value":448},"For example, FastAPI supports Python's ",{"type":23,"tag":147,"props":450,"children":452},{"className":451},[],[453],{"type":29,"value":302},{"type":29,"value":455},"/",{"type":23,"tag":147,"props":457,"children":459},{"className":458},[],[460],{"type":29,"value":461},"await",{"type":29,"value":463}," syntax:",{"type":23,"tag":141,"props":465,"children":467},{"className":143,"code":466,"language":12,"meta":8,"style":8},"@app.get(\"/items/{item_id}\")\nasync def read_item(item_id: int):\n    results = await async_database_call(item_id)\n    return results\n",[468],{"type":23,"tag":147,"props":469,"children":470},{"__ignoreMap":8},[471,502,532,555],{"type":23,"tag":151,"props":472,"children":473},{"class":153,"line":154},[474,479,483,488,493,498],{"type":23,"tag":151,"props":475,"children":476},{"style":199},[477],{"type":29,"value":478},"@app.get",{"type":23,"tag":151,"props":480,"children":481},{"style":164},[482],{"type":29,"value":207},{"type":23,"tag":151,"props":484,"children":485},{"style":285},[486],{"type":29,"value":487},"\"/items/",{"type":23,"tag":151,"props":489,"children":490},{"style":224},[491],{"type":29,"value":492},"{item_id}",{"type":23,"tag":151,"props":494,"children":495},{"style":285},[496],{"type":29,"value":497},"\"",{"type":23,"tag":151,"props":499,"children":500},{"style":164},[501],{"type":29,"value":293},{"type":23,"tag":151,"props":503,"children":504},{"class":153,"line":180},[505,509,513,518,523,528],{"type":23,"tag":151,"props":506,"children":507},{"style":158},[508],{"type":29,"value":302},{"type":23,"tag":151,"props":510,"children":511},{"style":158},[512],{"type":29,"value":307},{"type":23,"tag":151,"props":514,"children":515},{"style":199},[516],{"type":29,"value":517}," read_item",{"type":23,"tag":151,"props":519,"children":520},{"style":164},[521],{"type":29,"value":522},"(item_id: ",{"type":23,"tag":151,"props":524,"children":525},{"style":224},[526],{"type":29,"value":527},"int",{"type":23,"tag":151,"props":529,"children":530},{"style":164},[531],{"type":29,"value":217},{"type":23,"tag":151,"props":533,"children":534},{"class":153,"line":190},[535,540,545,550],{"type":23,"tag":151,"props":536,"children":537},{"style":164},[538],{"type":29,"value":539},"    results ",{"type":23,"tag":151,"props":541,"children":542},{"style":158},[543],{"type":29,"value":544},"=",{"type":23,"tag":151,"props":546,"children":547},{"style":158},[548],{"type":29,"value":549}," await",{"type":23,"tag":151,"props":551,"children":552},{"style":164},[553],{"type":29,"value":554}," async_database_call(item_id)\n",{"type":23,"tag":151,"props":556,"children":557},{"class":153,"line":220},[558,562],{"type":23,"tag":151,"props":559,"children":560},{"style":158},[561],{"type":29,"value":336},{"type":23,"tag":151,"props":563,"children":564},{"style":164},[565],{"type":29,"value":566}," results\n",{"type":23,"tag":117,"props":568,"children":570},{"id":569},"developer-friendly",[571],{"type":29,"value":572},"Developer Friendly",{"type":23,"tag":32,"props":574,"children":575},{},[576],{"type":29,"value":577},"FastAPI aims to provide a pleasant developer experience. It has an intuitive interface, helpful error messages, and encourages best practices that lead to maintainable code.",{"type":23,"tag":32,"props":579,"children":580},{},[581],{"type":29,"value":582},"For example, clear and explicit error messages help identify issues faster:",{"type":23,"tag":141,"props":584,"children":588},{"className":585,"code":587,"language":29},[586],"language-text","422 Unprocessable Entity\n\nbody validation error\nfield required (type=value_error.missing)\n\n> body\n  > item_id\n    field required (type=value_error.missing)\n",[589],{"type":23,"tag":147,"props":590,"children":591},{"__ignoreMap":8},[592],{"type":29,"value":587},{"type":23,"tag":117,"props":594,"children":596},{"id":595},"asgi",[597],{"type":29,"value":598},"ASGI",{"type":23,"tag":32,"props":600,"children":601},{},[602,608],{"type":23,"tag":36,"props":603,"children":606},{"href":604,"rel":605},"https://en.wikipedia.org/wiki/Asynchronous_Server_Gateway_Interface",[40],[607],{"type":29,"value":598},{"type":29,"value":609},", which stands for Asynchronous Server Gateway Interface, is a specification between web servers and Python web applications or frameworks to allow greater concurrency. It's a successor to WSGI, the Web Server Gateway Interface, which has been the standard for synchronous Python web applications.",{"type":23,"tag":32,"props":611,"children":612},{},[613],{"type":29,"value":614},"ASGI supports both synchronous and asynchronous communication, making it suitable for handling a large number of simultaneous incoming requests, particularly those that might spend a lot of time waiting, such as requests involving I/O operations.",{"type":23,"tag":24,"props":616,"children":618},{"id":617},"fastapi-vs-other-frameworks",[619],{"type":29,"value":620},"FastAPI vs Other Frameworks",{"type":23,"tag":32,"props":622,"children":623},{},[624],{"type":29,"value":625},"How does FastAPI compare to popular frameworks like Django and Flask? Let's take a look at some of the key differences.",{"type":23,"tag":117,"props":627,"children":629},{"id":628},"fastapi-vs-django",[630],{"type":29,"value":631},"FastAPI vs Django",{"type":23,"tag":32,"props":633,"children":634},{},[635],{"type":29,"value":636},"Django is a mature, full-featured framework with extensive documentation and an active community. However, it has a larger footprint and steeper learning curve than FastAPI.",{"type":23,"tag":32,"props":638,"children":639},{},[640],{"type":29,"value":641},"For example, Django's more complex project structure can make initial setup take longer, as well as requiring a deeper understanding of the framework's internals:",{"type":23,"tag":141,"props":643,"children":646},{"className":644,"code":645,"language":29},[586],"myproject/\n  manage.py\n  myproject/\n    __init__.py\n    settings.py\n    urls.py\n    asgi.py\n    wsgi.py\n  app/\n    __init__.py\n    admin.py\n    apps.py\n    models.py\n    tests.py\n    views.py\n",[647],{"type":23,"tag":147,"props":648,"children":649},{"__ignoreMap":8},[650],{"type":29,"value":645},{"type":23,"tag":32,"props":652,"children":653},{},[654],{"type":29,"value":655},"Whereas FastAPI can utilize a much simpler directory structure:",{"type":23,"tag":141,"props":657,"children":660},{"className":658,"code":659,"language":29},[586],"myapi/\n  main.py\n  models.py\n  schemas.py\n  api/\n    __init__.py\n    endpoints.py\n",[661],{"type":23,"tag":147,"props":662,"children":663},{"__ignoreMap":8},[664],{"type":29,"value":659},{"type":23,"tag":32,"props":666,"children":667},{},[668],{"type":29,"value":669},"Django's built-in ORM and admin interface are advantages for some projects. However, FastAPI's focus on API development makes it better suited for building lightweight, high-performance services. Django has a lot of features built-in that aren't needed in many applications. Django tries to give you everything a website COULD need, but that can be overkill for smaller projects or applications that require high performance, because you often don't NEED everything Django provides.",{"type":23,"tag":117,"props":671,"children":673},{"id":672},"fastapi-vs-flask",[674],{"type":29,"value":675},"FastAPI vs Flask",{"type":23,"tag":32,"props":677,"children":678},{},[679],{"type":29,"value":680},"Flask and FastAPI are more directly comparable compared to Django and FastAPI, as they are both microframeworks that don't have a lot of included bloat. Compared to Flask, FastAPI's built-in features make it easier to get started and maintain code. While Flask and FastAPI are both well-suited for building APIs, and both are similar in speed, with Flask being slightly faster, FastAPI's automatic data validation and documentation generation make it a better choice for API development, in my opinion. With Flask, validation and documentation generation requires extra libraries and boilerplate code.",{"type":23,"tag":32,"props":682,"children":683},{},[684],{"type":29,"value":685},"For example, validating a POST request in Flask:",{"type":23,"tag":141,"props":687,"children":689},{"className":143,"code":688,"language":12,"meta":8,"style":8},"from flask import request\nfrom werkzeug.exceptions import BadRequest\n\n@app.route(\"/users\", methods=[\"POST\"]) \ndef create_user():\n  data = request.get_json()\n\n  if not data:\n    return BadRequest(\"No JSON provided\")\n\n  if \"name\" not in data:\n    return BadRequest(\"Missing name\")\n\n  if not isinstance(data[\"name\"], str):\n    return BadRequest(\"Name must be a string\")\n\n  if \"age\" not in data:\n    return BadRequest(\"Missing age\")\n\n  if not isinstance(data[\"age\"], int):\n    return BadRequest(\"Age must be an integer\")\n\n  # ... etc for each field to be validated\n\n  # ... Create the user ...\n  \n  return user\n",[690],{"type":23,"tag":147,"props":691,"children":692},{"__ignoreMap":8},[693,714,735,742,787,804,821,828,846,867,874,899,920,928,969,990,998,1023,1044,1052,1089,1110,1118,1127,1135,1144,1153],{"type":23,"tag":151,"props":694,"children":695},{"class":153,"line":154},[696,700,705,709],{"type":23,"tag":151,"props":697,"children":698},{"style":158},[699],{"type":29,"value":161},{"type":23,"tag":151,"props":701,"children":702},{"style":164},[703],{"type":29,"value":704}," flask ",{"type":23,"tag":151,"props":706,"children":707},{"style":158},[708],{"type":29,"value":172},{"type":23,"tag":151,"props":710,"children":711},{"style":164},[712],{"type":29,"value":713}," request\n",{"type":23,"tag":151,"props":715,"children":716},{"class":153,"line":180},[717,721,726,730],{"type":23,"tag":151,"props":718,"children":719},{"style":158},[720],{"type":29,"value":161},{"type":23,"tag":151,"props":722,"children":723},{"style":164},[724],{"type":29,"value":725}," werkzeug.exceptions ",{"type":23,"tag":151,"props":727,"children":728},{"style":158},[729],{"type":29,"value":172},{"type":23,"tag":151,"props":731,"children":732},{"style":164},[733],{"type":29,"value":734}," BadRequest\n",{"type":23,"tag":151,"props":736,"children":737},{"class":153,"line":190},[738],{"type":23,"tag":151,"props":739,"children":740},{"emptyLinePlaceholder":184},[741],{"type":29,"value":187},{"type":23,"tag":151,"props":743,"children":744},{"class":153,"line":220},[745,750,754,758,762,768,772,777,782],{"type":23,"tag":151,"props":746,"children":747},{"style":199},[748],{"type":29,"value":749},"@app.route",{"type":23,"tag":151,"props":751,"children":752},{"style":164},[753],{"type":29,"value":207},{"type":23,"tag":151,"props":755,"children":756},{"style":285},[757],{"type":29,"value":288},{"type":23,"tag":151,"props":759,"children":760},{"style":164},[761],{"type":29,"value":370},{"type":23,"tag":151,"props":763,"children":765},{"style":764},"--shiki-default:#E36209;--shiki-dark:#FFAB70",[766],{"type":29,"value":767},"methods",{"type":23,"tag":151,"props":769,"children":770},{"style":158},[771],{"type":29,"value":544},{"type":23,"tag":151,"props":773,"children":774},{"style":164},[775],{"type":29,"value":776},"[",{"type":23,"tag":151,"props":778,"children":779},{"style":285},[780],{"type":29,"value":781},"\"POST\"",{"type":23,"tag":151,"props":783,"children":784},{"style":164},[785],{"type":29,"value":786},"]) \n",{"type":23,"tag":151,"props":788,"children":789},{"class":153,"line":240},[790,795,799],{"type":23,"tag":151,"props":791,"children":792},{"style":158},[793],{"type":29,"value":794},"def",{"type":23,"tag":151,"props":796,"children":797},{"style":199},[798],{"type":29,"value":312},{"type":23,"tag":151,"props":800,"children":801},{"style":164},[802],{"type":29,"value":803},"():\n",{"type":23,"tag":151,"props":805,"children":806},{"class":153,"line":254},[807,812,816],{"type":23,"tag":151,"props":808,"children":809},{"style":164},[810],{"type":29,"value":811},"  data ",{"type":23,"tag":151,"props":813,"children":814},{"style":158},[815],{"type":29,"value":544},{"type":23,"tag":151,"props":817,"children":818},{"style":164},[819],{"type":29,"value":820}," request.get_json()\n",{"type":23,"tag":151,"props":822,"children":823},{"class":153,"line":263},[824],{"type":23,"tag":151,"props":825,"children":826},{"emptyLinePlaceholder":184},[827],{"type":29,"value":187},{"type":23,"tag":151,"props":829,"children":830},{"class":153,"line":272},[831,836,841],{"type":23,"tag":151,"props":832,"children":833},{"style":158},[834],{"type":29,"value":835},"  if",{"type":23,"tag":151,"props":837,"children":838},{"style":158},[839],{"type":29,"value":840}," not",{"type":23,"tag":151,"props":842,"children":843},{"style":164},[844],{"type":29,"value":845}," data:\n",{"type":23,"tag":151,"props":847,"children":848},{"class":153,"line":296},[849,853,858,863],{"type":23,"tag":151,"props":850,"children":851},{"style":158},[852],{"type":29,"value":336},{"type":23,"tag":151,"props":854,"children":855},{"style":164},[856],{"type":29,"value":857}," BadRequest(",{"type":23,"tag":151,"props":859,"children":860},{"style":285},[861],{"type":29,"value":862},"\"No JSON provided\"",{"type":23,"tag":151,"props":864,"children":865},{"style":164},[866],{"type":29,"value":293},{"type":23,"tag":151,"props":868,"children":869},{"class":153,"line":320},[870],{"type":23,"tag":151,"props":871,"children":872},{"emptyLinePlaceholder":184},[873],{"type":29,"value":187},{"type":23,"tag":151,"props":875,"children":876},{"class":153,"line":330},[877,881,886,890,895],{"type":23,"tag":151,"props":878,"children":879},{"style":158},[880],{"type":29,"value":835},{"type":23,"tag":151,"props":882,"children":883},{"style":285},[884],{"type":29,"value":885}," \"name\"",{"type":23,"tag":151,"props":887,"children":888},{"style":158},[889],{"type":29,"value":840},{"type":23,"tag":151,"props":891,"children":892},{"style":158},[893],{"type":29,"value":894}," in",{"type":23,"tag":151,"props":896,"children":897},{"style":164},[898],{"type":29,"value":845},{"type":23,"tag":151,"props":900,"children":902},{"class":153,"line":901},12,[903,907,911,916],{"type":23,"tag":151,"props":904,"children":905},{"style":158},[906],{"type":29,"value":336},{"type":23,"tag":151,"props":908,"children":909},{"style":164},[910],{"type":29,"value":857},{"type":23,"tag":151,"props":912,"children":913},{"style":285},[914],{"type":29,"value":915},"\"Missing name\"",{"type":23,"tag":151,"props":917,"children":918},{"style":164},[919],{"type":29,"value":293},{"type":23,"tag":151,"props":921,"children":923},{"class":153,"line":922},13,[924],{"type":23,"tag":151,"props":925,"children":926},{"emptyLinePlaceholder":184},[927],{"type":29,"value":187},{"type":23,"tag":151,"props":929,"children":931},{"class":153,"line":930},14,[932,936,940,945,950,955,960,965],{"type":23,"tag":151,"props":933,"children":934},{"style":158},[935],{"type":29,"value":835},{"type":23,"tag":151,"props":937,"children":938},{"style":158},[939],{"type":29,"value":840},{"type":23,"tag":151,"props":941,"children":942},{"style":224},[943],{"type":29,"value":944}," isinstance",{"type":23,"tag":151,"props":946,"children":947},{"style":164},[948],{"type":29,"value":949},"(data[",{"type":23,"tag":151,"props":951,"children":952},{"style":285},[953],{"type":29,"value":954},"\"name\"",{"type":23,"tag":151,"props":956,"children":957},{"style":164},[958],{"type":29,"value":959},"], ",{"type":23,"tag":151,"props":961,"children":962},{"style":224},[963],{"type":29,"value":964},"str",{"type":23,"tag":151,"props":966,"children":967},{"style":164},[968],{"type":29,"value":217},{"type":23,"tag":151,"props":970,"children":972},{"class":153,"line":971},15,[973,977,981,986],{"type":23,"tag":151,"props":974,"children":975},{"style":158},[976],{"type":29,"value":336},{"type":23,"tag":151,"props":978,"children":979},{"style":164},[980],{"type":29,"value":857},{"type":23,"tag":151,"props":982,"children":983},{"style":285},[984],{"type":29,"value":985},"\"Name must be a string\"",{"type":23,"tag":151,"props":987,"children":988},{"style":164},[989],{"type":29,"value":293},{"type":23,"tag":151,"props":991,"children":993},{"class":153,"line":992},16,[994],{"type":23,"tag":151,"props":995,"children":996},{"emptyLinePlaceholder":184},[997],{"type":29,"value":187},{"type":23,"tag":151,"props":999,"children":1001},{"class":153,"line":1000},17,[1002,1006,1011,1015,1019],{"type":23,"tag":151,"props":1003,"children":1004},{"style":158},[1005],{"type":29,"value":835},{"type":23,"tag":151,"props":1007,"children":1008},{"style":285},[1009],{"type":29,"value":1010}," \"age\"",{"type":23,"tag":151,"props":1012,"children":1013},{"style":158},[1014],{"type":29,"value":840},{"type":23,"tag":151,"props":1016,"children":1017},{"style":158},[1018],{"type":29,"value":894},{"type":23,"tag":151,"props":1020,"children":1021},{"style":164},[1022],{"type":29,"value":845},{"type":23,"tag":151,"props":1024,"children":1026},{"class":153,"line":1025},18,[1027,1031,1035,1040],{"type":23,"tag":151,"props":1028,"children":1029},{"style":158},[1030],{"type":29,"value":336},{"type":23,"tag":151,"props":1032,"children":1033},{"style":164},[1034],{"type":29,"value":857},{"type":23,"tag":151,"props":1036,"children":1037},{"style":285},[1038],{"type":29,"value":1039},"\"Missing age\"",{"type":23,"tag":151,"props":1041,"children":1042},{"style":164},[1043],{"type":29,"value":293},{"type":23,"tag":151,"props":1045,"children":1047},{"class":153,"line":1046},19,[1048],{"type":23,"tag":151,"props":1049,"children":1050},{"emptyLinePlaceholder":184},[1051],{"type":29,"value":187},{"type":23,"tag":151,"props":1053,"children":1055},{"class":153,"line":1054},20,[1056,1060,1064,1068,1072,1077,1081,1085],{"type":23,"tag":151,"props":1057,"children":1058},{"style":158},[1059],{"type":29,"value":835},{"type":23,"tag":151,"props":1061,"children":1062},{"style":158},[1063],{"type":29,"value":840},{"type":23,"tag":151,"props":1065,"children":1066},{"style":224},[1067],{"type":29,"value":944},{"type":23,"tag":151,"props":1069,"children":1070},{"style":164},[1071],{"type":29,"value":949},{"type":23,"tag":151,"props":1073,"children":1074},{"style":285},[1075],{"type":29,"value":1076},"\"age\"",{"type":23,"tag":151,"props":1078,"children":1079},{"style":164},[1080],{"type":29,"value":959},{"type":23,"tag":151,"props":1082,"children":1083},{"style":224},[1084],{"type":29,"value":527},{"type":23,"tag":151,"props":1086,"children":1087},{"style":164},[1088],{"type":29,"value":217},{"type":23,"tag":151,"props":1090,"children":1092},{"class":153,"line":1091},21,[1093,1097,1101,1106],{"type":23,"tag":151,"props":1094,"children":1095},{"style":158},[1096],{"type":29,"value":336},{"type":23,"tag":151,"props":1098,"children":1099},{"style":164},[1100],{"type":29,"value":857},{"type":23,"tag":151,"props":1102,"children":1103},{"style":285},[1104],{"type":29,"value":1105},"\"Age must be an integer\"",{"type":23,"tag":151,"props":1107,"children":1108},{"style":164},[1109],{"type":29,"value":293},{"type":23,"tag":151,"props":1111,"children":1113},{"class":153,"line":1112},22,[1114],{"type":23,"tag":151,"props":1115,"children":1116},{"emptyLinePlaceholder":184},[1117],{"type":29,"value":187},{"type":23,"tag":151,"props":1119,"children":1121},{"class":153,"line":1120},23,[1122],{"type":23,"tag":151,"props":1123,"children":1124},{"style":324},[1125],{"type":29,"value":1126},"  # ... etc for each field to be validated\n",{"type":23,"tag":151,"props":1128,"children":1130},{"class":153,"line":1129},24,[1131],{"type":23,"tag":151,"props":1132,"children":1133},{"emptyLinePlaceholder":184},[1134],{"type":29,"value":187},{"type":23,"tag":151,"props":1136,"children":1138},{"class":153,"line":1137},25,[1139],{"type":23,"tag":151,"props":1140,"children":1141},{"style":324},[1142],{"type":29,"value":1143},"  # ... Create the user ...\n",{"type":23,"tag":151,"props":1145,"children":1147},{"class":153,"line":1146},26,[1148],{"type":23,"tag":151,"props":1149,"children":1150},{"style":164},[1151],{"type":29,"value":1152},"  \n",{"type":23,"tag":151,"props":1154,"children":1156},{"class":153,"line":1155},27,[1157,1162],{"type":23,"tag":151,"props":1158,"children":1159},{"style":158},[1160],{"type":29,"value":1161},"  return",{"type":23,"tag":151,"props":1163,"children":1164},{"style":164},[1165],{"type":29,"value":341},{"type":23,"tag":32,"props":1167,"children":1168},{},[1169],{"type":29,"value":1170},"The same validation in FastAPI:",{"type":23,"tag":141,"props":1172,"children":1174},{"className":143,"code":1173,"language":12,"meta":8,"style":8},"from pydantic import BaseModel\n\nclass User(BaseModel):\n  name: str\n  age: int\n  # ...etc\n\n@app.post(\"/users\")\nasync def create_user(user: User):\n   # All input data is validated against the User model automatically, returning a 422 error if validation fails\n\n   # ... Create the user ...\n\n  return user\n",[1175],{"type":23,"tag":147,"props":1176,"children":1177},{"__ignoreMap":8},[1178,1197,1204,1227,1239,1251,1259,1266,1285,1304,1312,1319,1327,1334],{"type":23,"tag":151,"props":1179,"children":1180},{"class":153,"line":154},[1181,1185,1189,1193],{"type":23,"tag":151,"props":1182,"children":1183},{"style":158},[1184],{"type":29,"value":161},{"type":23,"tag":151,"props":1186,"children":1187},{"style":164},[1188],{"type":29,"value":167},{"type":23,"tag":151,"props":1190,"children":1191},{"style":158},[1192],{"type":29,"value":172},{"type":23,"tag":151,"props":1194,"children":1195},{"style":164},[1196],{"type":29,"value":177},{"type":23,"tag":151,"props":1198,"children":1199},{"class":153,"line":180},[1200],{"type":23,"tag":151,"props":1201,"children":1202},{"emptyLinePlaceholder":184},[1203],{"type":29,"value":187},{"type":23,"tag":151,"props":1205,"children":1206},{"class":153,"line":190},[1207,1211,1215,1219,1223],{"type":23,"tag":151,"props":1208,"children":1209},{"style":158},[1210],{"type":29,"value":196},{"type":23,"tag":151,"props":1212,"children":1213},{"style":199},[1214],{"type":29,"value":202},{"type":23,"tag":151,"props":1216,"children":1217},{"style":164},[1218],{"type":29,"value":207},{"type":23,"tag":151,"props":1220,"children":1221},{"style":199},[1222],{"type":29,"value":212},{"type":23,"tag":151,"props":1224,"children":1225},{"style":164},[1226],{"type":29,"value":217},{"type":23,"tag":151,"props":1228,"children":1229},{"class":153,"line":220},[1230,1235],{"type":23,"tag":151,"props":1231,"children":1232},{"style":164},[1233],{"type":29,"value":1234},"  name: ",{"type":23,"tag":151,"props":1236,"children":1237},{"style":224},[1238],{"type":29,"value":251},{"type":23,"tag":151,"props":1240,"children":1241},{"class":153,"line":240},[1242,1247],{"type":23,"tag":151,"props":1243,"children":1244},{"style":164},[1245],{"type":29,"value":1246},"  age: ",{"type":23,"tag":151,"props":1248,"children":1249},{"style":224},[1250],{"type":29,"value":237},{"type":23,"tag":151,"props":1252,"children":1253},{"class":153,"line":254},[1254],{"type":23,"tag":151,"props":1255,"children":1256},{"style":324},[1257],{"type":29,"value":1258},"  # ...etc\n",{"type":23,"tag":151,"props":1260,"children":1261},{"class":153,"line":263},[1262],{"type":23,"tag":151,"props":1263,"children":1264},{"emptyLinePlaceholder":184},[1265],{"type":29,"value":187},{"type":23,"tag":151,"props":1267,"children":1268},{"class":153,"line":272},[1269,1273,1277,1281],{"type":23,"tag":151,"props":1270,"children":1271},{"style":199},[1272],{"type":29,"value":278},{"type":23,"tag":151,"props":1274,"children":1275},{"style":164},[1276],{"type":29,"value":207},{"type":23,"tag":151,"props":1278,"children":1279},{"style":285},[1280],{"type":29,"value":288},{"type":23,"tag":151,"props":1282,"children":1283},{"style":164},[1284],{"type":29,"value":293},{"type":23,"tag":151,"props":1286,"children":1287},{"class":153,"line":296},[1288,1292,1296,1300],{"type":23,"tag":151,"props":1289,"children":1290},{"style":158},[1291],{"type":29,"value":302},{"type":23,"tag":151,"props":1293,"children":1294},{"style":158},[1295],{"type":29,"value":307},{"type":23,"tag":151,"props":1297,"children":1298},{"style":199},[1299],{"type":29,"value":312},{"type":23,"tag":151,"props":1301,"children":1302},{"style":164},[1303],{"type":29,"value":317},{"type":23,"tag":151,"props":1305,"children":1306},{"class":153,"line":320},[1307],{"type":23,"tag":151,"props":1308,"children":1309},{"style":324},[1310],{"type":29,"value":1311},"   # All input data is validated against the User model automatically, returning a 422 error if validation fails\n",{"type":23,"tag":151,"props":1313,"children":1314},{"class":153,"line":330},[1315],{"type":23,"tag":151,"props":1316,"children":1317},{"emptyLinePlaceholder":184},[1318],{"type":29,"value":187},{"type":23,"tag":151,"props":1320,"children":1321},{"class":153,"line":901},[1322],{"type":23,"tag":151,"props":1323,"children":1324},{"style":324},[1325],{"type":29,"value":1326},"   # ... Create the user ...\n",{"type":23,"tag":151,"props":1328,"children":1329},{"class":153,"line":922},[1330],{"type":23,"tag":151,"props":1331,"children":1332},{"emptyLinePlaceholder":184},[1333],{"type":29,"value":187},{"type":23,"tag":151,"props":1335,"children":1336},{"class":153,"line":930},[1337,1341],{"type":23,"tag":151,"props":1338,"children":1339},{"style":158},[1340],{"type":29,"value":1161},{"type":23,"tag":151,"props":1342,"children":1343},{"style":164},[1344],{"type":29,"value":341},{"type":23,"tag":32,"props":1346,"children":1347},{},[1348],{"type":29,"value":1349},"FastAPI also allows for validating the output data, like so:",{"type":23,"tag":141,"props":1351,"children":1353},{"className":143,"code":1352,"language":12,"meta":8,"style":8},"from pydantic import BaseModel\n\nclass User(BaseModel):\n  name: str\n  age: int\n  # ...etc\n\nclass UserOut(BaseModel):\n  name: str\n  age: int\n  # ...etc\n\n@app.post(\"/users\", response_model=UserOut)\nasync def create_user(user: User):\n   # All input data is validated against the User model automatically, returning a 422 error if validation fails\n\n   # ... Create the user ...\n\n  return user\n",[1354],{"type":23,"tag":147,"props":1355,"children":1356},{"__ignoreMap":8},[1357,1376,1383,1406,1417,1428,1435,1442,1466,1477,1488,1495,1502,1535,1554,1561,1568,1575,1582],{"type":23,"tag":151,"props":1358,"children":1359},{"class":153,"line":154},[1360,1364,1368,1372],{"type":23,"tag":151,"props":1361,"children":1362},{"style":158},[1363],{"type":29,"value":161},{"type":23,"tag":151,"props":1365,"children":1366},{"style":164},[1367],{"type":29,"value":167},{"type":23,"tag":151,"props":1369,"children":1370},{"style":158},[1371],{"type":29,"value":172},{"type":23,"tag":151,"props":1373,"children":1374},{"style":164},[1375],{"type":29,"value":177},{"type":23,"tag":151,"props":1377,"children":1378},{"class":153,"line":180},[1379],{"type":23,"tag":151,"props":1380,"children":1381},{"emptyLinePlaceholder":184},[1382],{"type":29,"value":187},{"type":23,"tag":151,"props":1384,"children":1385},{"class":153,"line":190},[1386,1390,1394,1398,1402],{"type":23,"tag":151,"props":1387,"children":1388},{"style":158},[1389],{"type":29,"value":196},{"type":23,"tag":151,"props":1391,"children":1392},{"style":199},[1393],{"type":29,"value":202},{"type":23,"tag":151,"props":1395,"children":1396},{"style":164},[1397],{"type":29,"value":207},{"type":23,"tag":151,"props":1399,"children":1400},{"style":199},[1401],{"type":29,"value":212},{"type":23,"tag":151,"props":1403,"children":1404},{"style":164},[1405],{"type":29,"value":217},{"type":23,"tag":151,"props":1407,"children":1408},{"class":153,"line":220},[1409,1413],{"type":23,"tag":151,"props":1410,"children":1411},{"style":164},[1412],{"type":29,"value":1234},{"type":23,"tag":151,"props":1414,"children":1415},{"style":224},[1416],{"type":29,"value":251},{"type":23,"tag":151,"props":1418,"children":1419},{"class":153,"line":240},[1420,1424],{"type":23,"tag":151,"props":1421,"children":1422},{"style":164},[1423],{"type":29,"value":1246},{"type":23,"tag":151,"props":1425,"children":1426},{"style":224},[1427],{"type":29,"value":237},{"type":23,"tag":151,"props":1429,"children":1430},{"class":153,"line":254},[1431],{"type":23,"tag":151,"props":1432,"children":1433},{"style":324},[1434],{"type":29,"value":1258},{"type":23,"tag":151,"props":1436,"children":1437},{"class":153,"line":263},[1438],{"type":23,"tag":151,"props":1439,"children":1440},{"emptyLinePlaceholder":184},[1441],{"type":29,"value":187},{"type":23,"tag":151,"props":1443,"children":1444},{"class":153,"line":272},[1445,1449,1454,1458,1462],{"type":23,"tag":151,"props":1446,"children":1447},{"style":158},[1448],{"type":29,"value":196},{"type":23,"tag":151,"props":1450,"children":1451},{"style":199},[1452],{"type":29,"value":1453}," UserOut",{"type":23,"tag":151,"props":1455,"children":1456},{"style":164},[1457],{"type":29,"value":207},{"type":23,"tag":151,"props":1459,"children":1460},{"style":199},[1461],{"type":29,"value":212},{"type":23,"tag":151,"props":1463,"children":1464},{"style":164},[1465],{"type":29,"value":217},{"type":23,"tag":151,"props":1467,"children":1468},{"class":153,"line":296},[1469,1473],{"type":23,"tag":151,"props":1470,"children":1471},{"style":164},[1472],{"type":29,"value":1234},{"type":23,"tag":151,"props":1474,"children":1475},{"style":224},[1476],{"type":29,"value":251},{"type":23,"tag":151,"props":1478,"children":1479},{"class":153,"line":320},[1480,1484],{"type":23,"tag":151,"props":1481,"children":1482},{"style":164},[1483],{"type":29,"value":1246},{"type":23,"tag":151,"props":1485,"children":1486},{"style":224},[1487],{"type":29,"value":237},{"type":23,"tag":151,"props":1489,"children":1490},{"class":153,"line":330},[1491],{"type":23,"tag":151,"props":1492,"children":1493},{"style":324},[1494],{"type":29,"value":1258},{"type":23,"tag":151,"props":1496,"children":1497},{"class":153,"line":901},[1498],{"type":23,"tag":151,"props":1499,"children":1500},{"emptyLinePlaceholder":184},[1501],{"type":29,"value":187},{"type":23,"tag":151,"props":1503,"children":1504},{"class":153,"line":922},[1505,1509,1513,1517,1521,1526,1530],{"type":23,"tag":151,"props":1506,"children":1507},{"style":199},[1508],{"type":29,"value":278},{"type":23,"tag":151,"props":1510,"children":1511},{"style":164},[1512],{"type":29,"value":207},{"type":23,"tag":151,"props":1514,"children":1515},{"style":285},[1516],{"type":29,"value":288},{"type":23,"tag":151,"props":1518,"children":1519},{"style":164},[1520],{"type":29,"value":370},{"type":23,"tag":151,"props":1522,"children":1523},{"style":764},[1524],{"type":29,"value":1525},"response_model",{"type":23,"tag":151,"props":1527,"children":1528},{"style":158},[1529],{"type":29,"value":544},{"type":23,"tag":151,"props":1531,"children":1532},{"style":164},[1533],{"type":29,"value":1534},"UserOut)\n",{"type":23,"tag":151,"props":1536,"children":1537},{"class":153,"line":930},[1538,1542,1546,1550],{"type":23,"tag":151,"props":1539,"children":1540},{"style":158},[1541],{"type":29,"value":302},{"type":23,"tag":151,"props":1543,"children":1544},{"style":158},[1545],{"type":29,"value":307},{"type":23,"tag":151,"props":1547,"children":1548},{"style":199},[1549],{"type":29,"value":312},{"type":23,"tag":151,"props":1551,"children":1552},{"style":164},[1553],{"type":29,"value":317},{"type":23,"tag":151,"props":1555,"children":1556},{"class":153,"line":971},[1557],{"type":23,"tag":151,"props":1558,"children":1559},{"style":324},[1560],{"type":29,"value":1311},{"type":23,"tag":151,"props":1562,"children":1563},{"class":153,"line":992},[1564],{"type":23,"tag":151,"props":1565,"children":1566},{"emptyLinePlaceholder":184},[1567],{"type":29,"value":187},{"type":23,"tag":151,"props":1569,"children":1570},{"class":153,"line":1000},[1571],{"type":23,"tag":151,"props":1572,"children":1573},{"style":324},[1574],{"type":29,"value":1326},{"type":23,"tag":151,"props":1576,"children":1577},{"class":153,"line":1025},[1578],{"type":23,"tag":151,"props":1579,"children":1580},{"emptyLinePlaceholder":184},[1581],{"type":29,"value":187},{"type":23,"tag":151,"props":1583,"children":1584},{"class":153,"line":1046},[1585,1589],{"type":23,"tag":151,"props":1586,"children":1587},{"style":158},[1588],{"type":29,"value":1161},{"type":23,"tag":151,"props":1590,"children":1591},{"style":164},[1592],{"type":29,"value":341},{"type":23,"tag":32,"props":1594,"children":1595},{},[1596,1598,1603,1605,1611],{"type":29,"value":1597},"In this example, the ",{"type":23,"tag":147,"props":1599,"children":1601},{"className":1600},[],[1602],{"type":29,"value":1525},{"type":29,"value":1604}," parameter validates the output User object against the ",{"type":23,"tag":147,"props":1606,"children":1608},{"className":1607},[],[1609],{"type":29,"value":1610},"UserOut",{"type":29,"value":1612}," model, returning a 422 error if validation fails. This ensures that the output data matches the expected format, and also assists in generating more detailed documentation.",{"type":23,"tag":117,"props":1614,"children":1616},{"id":1615},"api-documentation-in-flask",[1617],{"type":29,"value":1618},"API Documentation in Flask",{"type":23,"tag":32,"props":1620,"children":1621},{},[1622],{"type":29,"value":1623},"Generating comprehensive API documentation in Flask requires installing extensions, configuring swagger, and decorating routes:",{"type":23,"tag":141,"props":1625,"children":1627},{"className":143,"code":1626,"language":12,"meta":8,"style":8},"from flask import Flask\nfrom flask_swagger_ui import get_swaggerui_blueprint\nfrom flasgger import Swagger, swag_from\n\napp = Flask(__name__)\n\n### Required Swagger setup ###\nSWAGGER_URL = '/api/docs'\nAPI_URL = '/static/swagger.json'\nSWAGGERUI_BLUEPRINT = get_swaggerui_blueprint(\n    SWAGGER_URL,\n    API_URL,\n    config={\n        'app_name': \"My App\"\n    }\n)\napp.register_blueprint(SWAGGERUI_BLUEPRINT, url_prefix=SWAGGER_URL)\n### End Swagger setup ###\n\n\n@app.route('/users', methods=['GET'])\n@swag_from('swagger/users.yml')\ndef get_users():\n  \"\"\"Gets list of users.\n  \n  ---\n  get:\n    description: Returns list of users\n    responses:\n      200:\n        description: Successful response\n        content:\n          application/json:\n            schema:\n              type: array\n              items:\n                type: object\n                properties:\n                  id: \n                    type: integer\n                  name:\n                    type: string\n  \"\"\"\n  pass \n\nif __name__ == \"__main__\":\n    app.run()\n",[1628],{"type":23,"tag":147,"props":1629,"children":1630},{"__ignoreMap":8},[1631,1651,1672,1693,1700,1726,1733,1741,1759,1776,1793,1806,1818,1835,1852,1860,1867,1900,1908,1915,1922,1964,1985,2001,2009,2016,2024,2032,2041,2050,2059,2068,2077,2086,2095,2104,2113,2122,2131,2140,2149,2158,2167,2176,2190,2198,2227],{"type":23,"tag":151,"props":1632,"children":1633},{"class":153,"line":154},[1634,1638,1642,1646],{"type":23,"tag":151,"props":1635,"children":1636},{"style":158},[1637],{"type":29,"value":161},{"type":23,"tag":151,"props":1639,"children":1640},{"style":164},[1641],{"type":29,"value":704},{"type":23,"tag":151,"props":1643,"children":1644},{"style":158},[1645],{"type":29,"value":172},{"type":23,"tag":151,"props":1647,"children":1648},{"style":164},[1649],{"type":29,"value":1650}," Flask\n",{"type":23,"tag":151,"props":1652,"children":1653},{"class":153,"line":180},[1654,1658,1663,1667],{"type":23,"tag":151,"props":1655,"children":1656},{"style":158},[1657],{"type":29,"value":161},{"type":23,"tag":151,"props":1659,"children":1660},{"style":164},[1661],{"type":29,"value":1662}," flask_swagger_ui ",{"type":23,"tag":151,"props":1664,"children":1665},{"style":158},[1666],{"type":29,"value":172},{"type":23,"tag":151,"props":1668,"children":1669},{"style":164},[1670],{"type":29,"value":1671}," get_swaggerui_blueprint\n",{"type":23,"tag":151,"props":1673,"children":1674},{"class":153,"line":190},[1675,1679,1684,1688],{"type":23,"tag":151,"props":1676,"children":1677},{"style":158},[1678],{"type":29,"value":161},{"type":23,"tag":151,"props":1680,"children":1681},{"style":164},[1682],{"type":29,"value":1683}," flasgger ",{"type":23,"tag":151,"props":1685,"children":1686},{"style":158},[1687],{"type":29,"value":172},{"type":23,"tag":151,"props":1689,"children":1690},{"style":164},[1691],{"type":29,"value":1692}," Swagger, swag_from\n",{"type":23,"tag":151,"props":1694,"children":1695},{"class":153,"line":220},[1696],{"type":23,"tag":151,"props":1697,"children":1698},{"emptyLinePlaceholder":184},[1699],{"type":29,"value":187},{"type":23,"tag":151,"props":1701,"children":1702},{"class":153,"line":240},[1703,1708,1712,1717,1722],{"type":23,"tag":151,"props":1704,"children":1705},{"style":164},[1706],{"type":29,"value":1707},"app ",{"type":23,"tag":151,"props":1709,"children":1710},{"style":158},[1711],{"type":29,"value":544},{"type":23,"tag":151,"props":1713,"children":1714},{"style":164},[1715],{"type":29,"value":1716}," Flask(",{"type":23,"tag":151,"props":1718,"children":1719},{"style":224},[1720],{"type":29,"value":1721},"__name__",{"type":23,"tag":151,"props":1723,"children":1724},{"style":164},[1725],{"type":29,"value":293},{"type":23,"tag":151,"props":1727,"children":1728},{"class":153,"line":254},[1729],{"type":23,"tag":151,"props":1730,"children":1731},{"emptyLinePlaceholder":184},[1732],{"type":29,"value":187},{"type":23,"tag":151,"props":1734,"children":1735},{"class":153,"line":263},[1736],{"type":23,"tag":151,"props":1737,"children":1738},{"style":324},[1739],{"type":29,"value":1740},"### Required Swagger setup ###\n",{"type":23,"tag":151,"props":1742,"children":1743},{"class":153,"line":272},[1744,1749,1754],{"type":23,"tag":151,"props":1745,"children":1746},{"style":224},[1747],{"type":29,"value":1748},"SWAGGER_URL",{"type":23,"tag":151,"props":1750,"children":1751},{"style":158},[1752],{"type":29,"value":1753}," =",{"type":23,"tag":151,"props":1755,"children":1756},{"style":285},[1757],{"type":29,"value":1758}," '/api/docs'\n",{"type":23,"tag":151,"props":1760,"children":1761},{"class":153,"line":296},[1762,1767,1771],{"type":23,"tag":151,"props":1763,"children":1764},{"style":224},[1765],{"type":29,"value":1766},"API_URL",{"type":23,"tag":151,"props":1768,"children":1769},{"style":158},[1770],{"type":29,"value":1753},{"type":23,"tag":151,"props":1772,"children":1773},{"style":285},[1774],{"type":29,"value":1775}," '/static/swagger.json'\n",{"type":23,"tag":151,"props":1777,"children":1778},{"class":153,"line":320},[1779,1784,1788],{"type":23,"tag":151,"props":1780,"children":1781},{"style":224},[1782],{"type":29,"value":1783},"SWAGGERUI_BLUEPRINT",{"type":23,"tag":151,"props":1785,"children":1786},{"style":158},[1787],{"type":29,"value":1753},{"type":23,"tag":151,"props":1789,"children":1790},{"style":164},[1791],{"type":29,"value":1792}," get_swaggerui_blueprint(\n",{"type":23,"tag":151,"props":1794,"children":1795},{"class":153,"line":330},[1796,1801],{"type":23,"tag":151,"props":1797,"children":1798},{"style":224},[1799],{"type":29,"value":1800},"    SWAGGER_URL",{"type":23,"tag":151,"props":1802,"children":1803},{"style":164},[1804],{"type":29,"value":1805},",\n",{"type":23,"tag":151,"props":1807,"children":1808},{"class":153,"line":901},[1809,1814],{"type":23,"tag":151,"props":1810,"children":1811},{"style":224},[1812],{"type":29,"value":1813},"    API_URL",{"type":23,"tag":151,"props":1815,"children":1816},{"style":164},[1817],{"type":29,"value":1805},{"type":23,"tag":151,"props":1819,"children":1820},{"class":153,"line":922},[1821,1826,1830],{"type":23,"tag":151,"props":1822,"children":1823},{"style":764},[1824],{"type":29,"value":1825},"    config",{"type":23,"tag":151,"props":1827,"children":1828},{"style":158},[1829],{"type":29,"value":544},{"type":23,"tag":151,"props":1831,"children":1832},{"style":164},[1833],{"type":29,"value":1834},"{\n",{"type":23,"tag":151,"props":1836,"children":1837},{"class":153,"line":930},[1838,1843,1847],{"type":23,"tag":151,"props":1839,"children":1840},{"style":285},[1841],{"type":29,"value":1842},"        'app_name'",{"type":23,"tag":151,"props":1844,"children":1845},{"style":164},[1846],{"type":29,"value":232},{"type":23,"tag":151,"props":1848,"children":1849},{"style":285},[1850],{"type":29,"value":1851},"\"My App\"\n",{"type":23,"tag":151,"props":1853,"children":1854},{"class":153,"line":971},[1855],{"type":23,"tag":151,"props":1856,"children":1857},{"style":164},[1858],{"type":29,"value":1859},"    }\n",{"type":23,"tag":151,"props":1861,"children":1862},{"class":153,"line":992},[1863],{"type":23,"tag":151,"props":1864,"children":1865},{"style":164},[1866],{"type":29,"value":293},{"type":23,"tag":151,"props":1868,"children":1869},{"class":153,"line":1000},[1870,1875,1879,1883,1888,1892,1896],{"type":23,"tag":151,"props":1871,"children":1872},{"style":164},[1873],{"type":29,"value":1874},"app.register_blueprint(",{"type":23,"tag":151,"props":1876,"children":1877},{"style":224},[1878],{"type":29,"value":1783},{"type":23,"tag":151,"props":1880,"children":1881},{"style":164},[1882],{"type":29,"value":370},{"type":23,"tag":151,"props":1884,"children":1885},{"style":764},[1886],{"type":29,"value":1887},"url_prefix",{"type":23,"tag":151,"props":1889,"children":1890},{"style":158},[1891],{"type":29,"value":544},{"type":23,"tag":151,"props":1893,"children":1894},{"style":224},[1895],{"type":29,"value":1748},{"type":23,"tag":151,"props":1897,"children":1898},{"style":164},[1899],{"type":29,"value":293},{"type":23,"tag":151,"props":1901,"children":1902},{"class":153,"line":1025},[1903],{"type":23,"tag":151,"props":1904,"children":1905},{"style":324},[1906],{"type":29,"value":1907},"### End Swagger setup ###\n",{"type":23,"tag":151,"props":1909,"children":1910},{"class":153,"line":1046},[1911],{"type":23,"tag":151,"props":1912,"children":1913},{"emptyLinePlaceholder":184},[1914],{"type":29,"value":187},{"type":23,"tag":151,"props":1916,"children":1917},{"class":153,"line":1054},[1918],{"type":23,"tag":151,"props":1919,"children":1920},{"emptyLinePlaceholder":184},[1921],{"type":29,"value":187},{"type":23,"tag":151,"props":1923,"children":1924},{"class":153,"line":1091},[1925,1929,1933,1938,1942,1946,1950,1954,1959],{"type":23,"tag":151,"props":1926,"children":1927},{"style":199},[1928],{"type":29,"value":749},{"type":23,"tag":151,"props":1930,"children":1931},{"style":164},[1932],{"type":29,"value":207},{"type":23,"tag":151,"props":1934,"children":1935},{"style":285},[1936],{"type":29,"value":1937},"'/users'",{"type":23,"tag":151,"props":1939,"children":1940},{"style":164},[1941],{"type":29,"value":370},{"type":23,"tag":151,"props":1943,"children":1944},{"style":764},[1945],{"type":29,"value":767},{"type":23,"tag":151,"props":1947,"children":1948},{"style":158},[1949],{"type":29,"value":544},{"type":23,"tag":151,"props":1951,"children":1952},{"style":164},[1953],{"type":29,"value":776},{"type":23,"tag":151,"props":1955,"children":1956},{"style":285},[1957],{"type":29,"value":1958},"'GET'",{"type":23,"tag":151,"props":1960,"children":1961},{"style":164},[1962],{"type":29,"value":1963},"])\n",{"type":23,"tag":151,"props":1965,"children":1966},{"class":153,"line":1112},[1967,1972,1976,1981],{"type":23,"tag":151,"props":1968,"children":1969},{"style":199},[1970],{"type":29,"value":1971},"@swag_from",{"type":23,"tag":151,"props":1973,"children":1974},{"style":164},[1975],{"type":29,"value":207},{"type":23,"tag":151,"props":1977,"children":1978},{"style":285},[1979],{"type":29,"value":1980},"'swagger/users.yml'",{"type":23,"tag":151,"props":1982,"children":1983},{"style":164},[1984],{"type":29,"value":293},{"type":23,"tag":151,"props":1986,"children":1987},{"class":153,"line":1120},[1988,1992,1997],{"type":23,"tag":151,"props":1989,"children":1990},{"style":158},[1991],{"type":29,"value":794},{"type":23,"tag":151,"props":1993,"children":1994},{"style":199},[1995],{"type":29,"value":1996}," get_users",{"type":23,"tag":151,"props":1998,"children":1999},{"style":164},[2000],{"type":29,"value":803},{"type":23,"tag":151,"props":2002,"children":2003},{"class":153,"line":1129},[2004],{"type":23,"tag":151,"props":2005,"children":2006},{"style":285},[2007],{"type":29,"value":2008},"  \"\"\"Gets list of users.\n",{"type":23,"tag":151,"props":2010,"children":2011},{"class":153,"line":1137},[2012],{"type":23,"tag":151,"props":2013,"children":2014},{"style":285},[2015],{"type":29,"value":1152},{"type":23,"tag":151,"props":2017,"children":2018},{"class":153,"line":1146},[2019],{"type":23,"tag":151,"props":2020,"children":2021},{"style":285},[2022],{"type":29,"value":2023},"  ---\n",{"type":23,"tag":151,"props":2025,"children":2026},{"class":153,"line":1155},[2027],{"type":23,"tag":151,"props":2028,"children":2029},{"style":285},[2030],{"type":29,"value":2031},"  get:\n",{"type":23,"tag":151,"props":2033,"children":2035},{"class":153,"line":2034},28,[2036],{"type":23,"tag":151,"props":2037,"children":2038},{"style":285},[2039],{"type":29,"value":2040},"    description: Returns list of users\n",{"type":23,"tag":151,"props":2042,"children":2044},{"class":153,"line":2043},29,[2045],{"type":23,"tag":151,"props":2046,"children":2047},{"style":285},[2048],{"type":29,"value":2049},"    responses:\n",{"type":23,"tag":151,"props":2051,"children":2053},{"class":153,"line":2052},30,[2054],{"type":23,"tag":151,"props":2055,"children":2056},{"style":285},[2057],{"type":29,"value":2058},"      200:\n",{"type":23,"tag":151,"props":2060,"children":2062},{"class":153,"line":2061},31,[2063],{"type":23,"tag":151,"props":2064,"children":2065},{"style":285},[2066],{"type":29,"value":2067},"        description: Successful response\n",{"type":23,"tag":151,"props":2069,"children":2071},{"class":153,"line":2070},32,[2072],{"type":23,"tag":151,"props":2073,"children":2074},{"style":285},[2075],{"type":29,"value":2076},"        content:\n",{"type":23,"tag":151,"props":2078,"children":2080},{"class":153,"line":2079},33,[2081],{"type":23,"tag":151,"props":2082,"children":2083},{"style":285},[2084],{"type":29,"value":2085},"          application/json:\n",{"type":23,"tag":151,"props":2087,"children":2089},{"class":153,"line":2088},34,[2090],{"type":23,"tag":151,"props":2091,"children":2092},{"style":285},[2093],{"type":29,"value":2094},"            schema:\n",{"type":23,"tag":151,"props":2096,"children":2098},{"class":153,"line":2097},35,[2099],{"type":23,"tag":151,"props":2100,"children":2101},{"style":285},[2102],{"type":29,"value":2103},"              type: array\n",{"type":23,"tag":151,"props":2105,"children":2107},{"class":153,"line":2106},36,[2108],{"type":23,"tag":151,"props":2109,"children":2110},{"style":285},[2111],{"type":29,"value":2112},"              items:\n",{"type":23,"tag":151,"props":2114,"children":2116},{"class":153,"line":2115},37,[2117],{"type":23,"tag":151,"props":2118,"children":2119},{"style":285},[2120],{"type":29,"value":2121},"                type: object\n",{"type":23,"tag":151,"props":2123,"children":2125},{"class":153,"line":2124},38,[2126],{"type":23,"tag":151,"props":2127,"children":2128},{"style":285},[2129],{"type":29,"value":2130},"                properties:\n",{"type":23,"tag":151,"props":2132,"children":2134},{"class":153,"line":2133},39,[2135],{"type":23,"tag":151,"props":2136,"children":2137},{"style":285},[2138],{"type":29,"value":2139},"                  id: \n",{"type":23,"tag":151,"props":2141,"children":2143},{"class":153,"line":2142},40,[2144],{"type":23,"tag":151,"props":2145,"children":2146},{"style":285},[2147],{"type":29,"value":2148},"                    type: integer\n",{"type":23,"tag":151,"props":2150,"children":2152},{"class":153,"line":2151},41,[2153],{"type":23,"tag":151,"props":2154,"children":2155},{"style":285},[2156],{"type":29,"value":2157},"                  name:\n",{"type":23,"tag":151,"props":2159,"children":2161},{"class":153,"line":2160},42,[2162],{"type":23,"tag":151,"props":2163,"children":2164},{"style":285},[2165],{"type":29,"value":2166},"                    type: string\n",{"type":23,"tag":151,"props":2168,"children":2170},{"class":153,"line":2169},43,[2171],{"type":23,"tag":151,"props":2172,"children":2173},{"style":285},[2174],{"type":29,"value":2175},"  \"\"\"\n",{"type":23,"tag":151,"props":2177,"children":2179},{"class":153,"line":2178},44,[2180,2185],{"type":23,"tag":151,"props":2181,"children":2182},{"style":158},[2183],{"type":29,"value":2184},"  pass",{"type":23,"tag":151,"props":2186,"children":2187},{"style":164},[2188],{"type":29,"value":2189}," \n",{"type":23,"tag":151,"props":2191,"children":2193},{"class":153,"line":2192},45,[2194],{"type":23,"tag":151,"props":2195,"children":2196},{"emptyLinePlaceholder":184},[2197],{"type":29,"value":187},{"type":23,"tag":151,"props":2199,"children":2201},{"class":153,"line":2200},46,[2202,2207,2212,2217,2222],{"type":23,"tag":151,"props":2203,"children":2204},{"style":158},[2205],{"type":29,"value":2206},"if",{"type":23,"tag":151,"props":2208,"children":2209},{"style":224},[2210],{"type":29,"value":2211}," __name__",{"type":23,"tag":151,"props":2213,"children":2214},{"style":158},[2215],{"type":29,"value":2216}," ==",{"type":23,"tag":151,"props":2218,"children":2219},{"style":285},[2220],{"type":29,"value":2221}," \"__main__\"",{"type":23,"tag":151,"props":2223,"children":2224},{"style":164},[2225],{"type":29,"value":2226},":\n",{"type":23,"tag":151,"props":2228,"children":2230},{"class":153,"line":2229},47,[2231],{"type":23,"tag":151,"props":2232,"children":2233},{"style":164},[2234],{"type":29,"value":2235},"    app.run()\n",{"type":23,"tag":32,"props":2237,"children":2238},{},[2239,2241,2247],{"type":29,"value":2240},"The Swagger spec must be explicitly configured and ",{"type":23,"tag":147,"props":2242,"children":2244},{"className":2243},[],[2245],{"type":29,"value":2246},"flask-swagger-ui",{"type":29,"value":2248}," registered to serve docs. API routes require decorators to define schema and parameters. This is a lot of overhead to keep documentation in sync.",{"type":23,"tag":117,"props":2250,"children":2252},{"id":2251},"api-documentation-in-fastapi",[2253],{"type":29,"value":2254},"API Documentation in FastAPI",{"type":23,"tag":32,"props":2256,"children":2257},{},[2258],{"type":29,"value":2259},"FastAPI automatically generates OpenAPI schemas and Swagger UI interactive documentation:",{"type":23,"tag":141,"props":2261,"children":2263},{"className":143,"code":2262,"language":12,"meta":8,"style":8},"from fastapi import FastAPI\n\napp = FastAPI()\n\nclass UserOut(BaseModel):\n  id: int\n  name: str\n\n@app.get(\"/users\", response_model=list[UserOut])\nasync def get_users():\n    return [{\"id\": 1, \"name\": \"John\"}]\n\nif __name__ == \"__main__\":\n    import uvicorn\n    uvicorn.run(app)\n",[2264],{"type":23,"tag":147,"props":2265,"children":2266},{"__ignoreMap":8},[2267,2288,2295,2311,2318,2341,2357,2368,2375,2407,2426,2474,2481,2504,2517],{"type":23,"tag":151,"props":2268,"children":2269},{"class":153,"line":154},[2270,2274,2279,2283],{"type":23,"tag":151,"props":2271,"children":2272},{"style":158},[2273],{"type":29,"value":161},{"type":23,"tag":151,"props":2275,"children":2276},{"style":164},[2277],{"type":29,"value":2278}," fastapi ",{"type":23,"tag":151,"props":2280,"children":2281},{"style":158},[2282],{"type":29,"value":172},{"type":23,"tag":151,"props":2284,"children":2285},{"style":164},[2286],{"type":29,"value":2287}," FastAPI\n",{"type":23,"tag":151,"props":2289,"children":2290},{"class":153,"line":180},[2291],{"type":23,"tag":151,"props":2292,"children":2293},{"emptyLinePlaceholder":184},[2294],{"type":29,"value":187},{"type":23,"tag":151,"props":2296,"children":2297},{"class":153,"line":190},[2298,2302,2306],{"type":23,"tag":151,"props":2299,"children":2300},{"style":164},[2301],{"type":29,"value":1707},{"type":23,"tag":151,"props":2303,"children":2304},{"style":158},[2305],{"type":29,"value":544},{"type":23,"tag":151,"props":2307,"children":2308},{"style":164},[2309],{"type":29,"value":2310}," FastAPI()\n",{"type":23,"tag":151,"props":2312,"children":2313},{"class":153,"line":220},[2314],{"type":23,"tag":151,"props":2315,"children":2316},{"emptyLinePlaceholder":184},[2317],{"type":29,"value":187},{"type":23,"tag":151,"props":2319,"children":2320},{"class":153,"line":240},[2321,2325,2329,2333,2337],{"type":23,"tag":151,"props":2322,"children":2323},{"style":158},[2324],{"type":29,"value":196},{"type":23,"tag":151,"props":2326,"children":2327},{"style":199},[2328],{"type":29,"value":1453},{"type":23,"tag":151,"props":2330,"children":2331},{"style":164},[2332],{"type":29,"value":207},{"type":23,"tag":151,"props":2334,"children":2335},{"style":199},[2336],{"type":29,"value":212},{"type":23,"tag":151,"props":2338,"children":2339},{"style":164},[2340],{"type":29,"value":217},{"type":23,"tag":151,"props":2342,"children":2343},{"class":153,"line":254},[2344,2349,2353],{"type":23,"tag":151,"props":2345,"children":2346},{"style":224},[2347],{"type":29,"value":2348},"  id",{"type":23,"tag":151,"props":2350,"children":2351},{"style":164},[2352],{"type":29,"value":232},{"type":23,"tag":151,"props":2354,"children":2355},{"style":224},[2356],{"type":29,"value":237},{"type":23,"tag":151,"props":2358,"children":2359},{"class":153,"line":263},[2360,2364],{"type":23,"tag":151,"props":2361,"children":2362},{"style":164},[2363],{"type":29,"value":1234},{"type":23,"tag":151,"props":2365,"children":2366},{"style":224},[2367],{"type":29,"value":251},{"type":23,"tag":151,"props":2369,"children":2370},{"class":153,"line":272},[2371],{"type":23,"tag":151,"props":2372,"children":2373},{"emptyLinePlaceholder":184},[2374],{"type":29,"value":187},{"type":23,"tag":151,"props":2376,"children":2377},{"class":153,"line":296},[2378,2382,2386,2390,2394,2398,2402],{"type":23,"tag":151,"props":2379,"children":2380},{"style":199},[2381],{"type":29,"value":478},{"type":23,"tag":151,"props":2383,"children":2384},{"style":164},[2385],{"type":29,"value":207},{"type":23,"tag":151,"props":2387,"children":2388},{"style":285},[2389],{"type":29,"value":288},{"type":23,"tag":151,"props":2391,"children":2392},{"style":164},[2393],{"type":29,"value":370},{"type":23,"tag":151,"props":2395,"children":2396},{"style":764},[2397],{"type":29,"value":1525},{"type":23,"tag":151,"props":2399,"children":2400},{"style":158},[2401],{"type":29,"value":544},{"type":23,"tag":151,"props":2403,"children":2404},{"style":164},[2405],{"type":29,"value":2406},"list[UserOut])\n",{"type":23,"tag":151,"props":2408,"children":2409},{"class":153,"line":320},[2410,2414,2418,2422],{"type":23,"tag":151,"props":2411,"children":2412},{"style":158},[2413],{"type":29,"value":302},{"type":23,"tag":151,"props":2415,"children":2416},{"style":158},[2417],{"type":29,"value":307},{"type":23,"tag":151,"props":2419,"children":2420},{"style":199},[2421],{"type":29,"value":1996},{"type":23,"tag":151,"props":2423,"children":2424},{"style":164},[2425],{"type":29,"value":803},{"type":23,"tag":151,"props":2427,"children":2428},{"class":153,"line":330},[2429,2433,2438,2443,2447,2452,2456,2460,2464,2469],{"type":23,"tag":151,"props":2430,"children":2431},{"style":158},[2432],{"type":29,"value":336},{"type":23,"tag":151,"props":2434,"children":2435},{"style":164},[2436],{"type":29,"value":2437}," [{",{"type":23,"tag":151,"props":2439,"children":2440},{"style":285},[2441],{"type":29,"value":2442},"\"id\"",{"type":23,"tag":151,"props":2444,"children":2445},{"style":164},[2446],{"type":29,"value":232},{"type":23,"tag":151,"props":2448,"children":2449},{"style":224},[2450],{"type":29,"value":2451},"1",{"type":23,"tag":151,"props":2453,"children":2454},{"style":164},[2455],{"type":29,"value":370},{"type":23,"tag":151,"props":2457,"children":2458},{"style":285},[2459],{"type":29,"value":954},{"type":23,"tag":151,"props":2461,"children":2462},{"style":164},[2463],{"type":29,"value":232},{"type":23,"tag":151,"props":2465,"children":2466},{"style":285},[2467],{"type":29,"value":2468},"\"John\"",{"type":23,"tag":151,"props":2470,"children":2471},{"style":164},[2472],{"type":29,"value":2473},"}]\n",{"type":23,"tag":151,"props":2475,"children":2476},{"class":153,"line":901},[2477],{"type":23,"tag":151,"props":2478,"children":2479},{"emptyLinePlaceholder":184},[2480],{"type":29,"value":187},{"type":23,"tag":151,"props":2482,"children":2483},{"class":153,"line":922},[2484,2488,2492,2496,2500],{"type":23,"tag":151,"props":2485,"children":2486},{"style":158},[2487],{"type":29,"value":2206},{"type":23,"tag":151,"props":2489,"children":2490},{"style":224},[2491],{"type":29,"value":2211},{"type":23,"tag":151,"props":2493,"children":2494},{"style":158},[2495],{"type":29,"value":2216},{"type":23,"tag":151,"props":2497,"children":2498},{"style":285},[2499],{"type":29,"value":2221},{"type":23,"tag":151,"props":2501,"children":2502},{"style":164},[2503],{"type":29,"value":2226},{"type":23,"tag":151,"props":2505,"children":2506},{"class":153,"line":930},[2507,2512],{"type":23,"tag":151,"props":2508,"children":2509},{"style":158},[2510],{"type":29,"value":2511},"    import",{"type":23,"tag":151,"props":2513,"children":2514},{"style":164},[2515],{"type":29,"value":2516}," uvicorn\n",{"type":23,"tag":151,"props":2518,"children":2519},{"class":153,"line":971},[2520],{"type":23,"tag":151,"props":2521,"children":2522},{"style":164},[2523],{"type":29,"value":2524},"    uvicorn.run(app)\n",{"type":23,"tag":32,"props":2526,"children":2527},{},[2528],{"type":29,"value":2529},"That's it! FastAPI parses the function signatures and return types to build a live Swagger UI. Documentation always stays up-to-date with the code. The developer experience is greatly improved compared to manually defining schemas in Flask.",{"type":23,"tag":24,"props":2531,"children":2533},{"id":2532},"first-impressions-of-fastapi",[2534],{"type":29,"value":2535},"First Impressions of FastAPI",{"type":23,"tag":32,"props":2537,"children":2538},{},[2539],{"type":29,"value":2540},"After building an initial API with FastAPI, here are some thoughts:",{"type":23,"tag":117,"props":2542,"children":2544},{"id":2543},"quick-setup",[2545],{"type":29,"value":2546},"Quick Setup",{"type":23,"tag":32,"props":2548,"children":2549},{},[2550],{"type":29,"value":2551},"FastAPI was extremely quick to install and start using. The ability to use it directly without an application factory or settings file accelerated development.",{"type":23,"tag":117,"props":2553,"children":2555},{"id":2554},"intuitive-syntax",[2556],{"type":29,"value":2557},"Intuitive Syntax",{"type":23,"tag":32,"props":2559,"children":2560},{},[2561],{"type":29,"value":2562},"The syntax for creating endpoints, validating parameters, and configuring docs made sense right away. Python type hints integrate directly for validation.",{"type":23,"tag":117,"props":2564,"children":2566},{"id":2565},"automatic-documentation",[2567],{"type":29,"value":2568},"Automatic Documentation",{"type":23,"tag":32,"props":2570,"children":2571},{},[2572],{"type":29,"value":2573},"Generating interactive API docs using Swagger UI required no extra configuration. It was easy to immediately test endpoints and review parameters.",{"type":23,"tag":117,"props":2575,"children":2577},{"id":2576},"pydantic-data-validation",[2578],{"type":29,"value":2579},"Pydantic Data Validation",{"type":23,"tag":32,"props":2581,"children":2582},{},[2583],{"type":29,"value":2584},"Leveraging Pydantic for data validation using Python type hints reduced boilerplate code tremendously. Input and output was validated automatically.",{"type":23,"tag":117,"props":2586,"children":2588},{"id":2587},"performance",[2589],{"type":29,"value":2590},"Performance",{"type":23,"tag":32,"props":2592,"children":2593},{},[2594,2596,2603],{"type":29,"value":2595},"In ",{"type":23,"tag":36,"props":2597,"children":2600},{"href":2598,"rel":2599},"https://www.techempower.com/benchmarks/#section=data-r21&test=composite",[40],[2601],{"type":29,"value":2602},"benchmarks",{"type":29,"value":2604},", FastAPI achieved a score of 1184 compared to Django's 274, and Flask's 1229. This makes it a strong choice for high-performance web applications that don't require the speed of a lower-level language like Go or Rust, like typical APIs and microservices.",{"type":23,"tag":117,"props":2606,"children":2608},{"id":2607},"balance-of-flexibility-and-structure",[2609],{"type":29,"value":2610},"Balance of Flexibility and Structure",{"type":23,"tag":32,"props":2612,"children":2613},{},[2614],{"type":29,"value":2615},"While slightly opinionated in some areas like project structure, FastAPI still provided freedom to organize code beyond the basics as needed.",{"type":23,"tag":117,"props":2617,"children":2619},{"id":2618},"positive-first-impressions",[2620],{"type":29,"value":2621},"Positive First Impressions",{"type":23,"tag":32,"props":2623,"children":2624},{},[2625],{"type":29,"value":2626},"After creating several APIs with FastAPI, I'm impressed with FastAPI's performance, automatic validation, docs generation, and overall developer experience. The community and ecosystem are growing rapidly as well.",{"type":23,"tag":24,"props":2628,"children":2630},{"id":2629},"when-to-use-fastapi",[2631],{"type":29,"value":2632},"When to Use FastAPI",{"type":23,"tag":32,"props":2634,"children":2635},{},[2636],{"type":29,"value":2637},"Based on its strengths and comparisons to alternatives, here are some good use cases for FastAPI:",{"type":23,"tag":2639,"props":2640,"children":2641},"ul",{},[2642,2654,2664,2674,2684],{"type":23,"tag":2643,"props":2644,"children":2645},"li",{},[2646,2652],{"type":23,"tag":2647,"props":2648,"children":2649},"strong",{},[2650],{"type":29,"value":2651},"APIs and Microservices",{"type":29,"value":2653},": FastAPI is purpose-built for API development. Its performance and dev experience make it great for microservices too.",{"type":23,"tag":2643,"props":2655,"children":2656},{},[2657,2662],{"type":23,"tag":2647,"props":2658,"children":2659},{},[2660],{"type":29,"value":2661},"Real-time Applications",{"type":29,"value":2663},": FastAPI's ASGI support makes it a strong choice for apps requiring low latency like live streaming, gaming, and messaging.",{"type":23,"tag":2643,"props":2665,"children":2666},{},[2667,2672],{"type":23,"tag":2647,"props":2668,"children":2669},{},[2670],{"type":29,"value":2671},"Machine Learning",{"type":29,"value":2673},": For ML applications processing lots of data, FastAPI provides speed and reliability.",{"type":23,"tag":2643,"props":2675,"children":2676},{},[2677,2682],{"type":23,"tag":2647,"props":2678,"children":2679},{},[2680],{"type":29,"value":2681},"Legacy Modernization",{"type":29,"value":2683},": You can use FastAPI to create a modern API frontend for legacy systems and improve performance.",{"type":23,"tag":2643,"props":2685,"children":2686},{},[2687,2692],{"type":23,"tag":2647,"props":2688,"children":2689},{},[2690],{"type":29,"value":2691},"Prototyping and MVPs",{"type":29,"value":2693},": FastAPI's ability to create APIs and docs quickly accelerates prototyping and building minimal viable products.",{"type":23,"tag":24,"props":2695,"children":2697},{"id":2696},"real-world-usage",[2698],{"type":29,"value":2699},"Real-World Usage",{"type":23,"tag":32,"props":2701,"children":2702},{},[2703],{"type":29,"value":2704},"Several companies and organizations have adopted FastAPI for their projects, demonstrating its effectiveness and versatility in real-world use cases. Some examples include:",{"type":23,"tag":2639,"props":2706,"children":2707},{},[2708,2727,2745,2755],{"type":23,"tag":2643,"props":2709,"children":2710},{},[2711,2716,2718,2725],{"type":23,"tag":2647,"props":2712,"children":2713},{},[2714],{"type":29,"value":2715},"Uber",{"type":29,"value":2717},": Uber uses FastAPI in their internal projects to build and maintain high-performance APIs. FastAPI's performance and ease of use make it a suitable choice for Uber's large-scale applications that need to handle a high volume of requests efficiently. Uber uses FastAPI in their ",{"type":23,"tag":36,"props":2719,"children":2722},{"href":2720,"rel":2721},"https://github.com/uber/ludwig",[40],[2723],{"type":29,"value":2724},"Ludwig machine learning framework",{"type":29,"value":2726},", a deep learning toolbox.",{"type":23,"tag":2643,"props":2728,"children":2729},{},[2730,2735,2737,2744],{"type":23,"tag":2647,"props":2731,"children":2732},{},[2733],{"type":29,"value":2734},"Netflix",{"type":29,"value":2736},": Netflix uses FastAPI for some of their backend services. FastAPI's performance and support for asynchronous programming help Netflix build services that are capable of handling large amounts of data and concurrent connections. Netflix uses FastAPI for their open-source crisis management orchestration framework, ",{"type":23,"tag":36,"props":2738,"children":2741},{"href":2739,"rel":2740},"https://netflix.github.io/dispatch/",[40],[2742],{"type":29,"value":2743},"Dispatch",{"type":29,"value":386},{"type":23,"tag":2643,"props":2746,"children":2747},{},[2748,2753],{"type":23,"tag":2647,"props":2749,"children":2750},{},[2751],{"type":29,"value":2752},"Microsoft",{"type":29,"value":2754},": Microsoft utilizes FastAPI in some of their internal projects, taking advantage of its performance, data validation, and automatic documentation generation capabilities. FastAPI's features help Microsoft's development teams build APIs and services that are efficient, robust, and easily maintainable. Some machine learning related services that are made with FastAPI are being integrated into core Windows and Office products.",{"type":23,"tag":2643,"props":2756,"children":2757},{},[2758,2763,2765,2769,2771,2774,2776,2779],{"type":23,"tag":2647,"props":2759,"children":2760},{},[2761],{"type":29,"value":2762},"Art & Logic",{"type":29,"value":2764},": At Art+Logic, I have started utilizing FastAPI for various projects, including new applications and supporting older applications.",{"type":23,"tag":2766,"props":2767,"children":2768},"br",{},[],{"type":29,"value":2770},"In one particular project, we were working with a legacy application built on WebSauna, a Pyramid-based framework. The existing system relied on server-side rendering for its pages, which posed a challenge when developing a native iOS application, as we needed to create API endpoints for it. We were faced with two choices: either expand the existing WebSauna application by adding RESTful API endpoints or implement an API sidecar using another framework.",{"type":23,"tag":2766,"props":2772,"children":2773},{},[],{"type":29,"value":2775},"Given that WebSauna is a legacy framework with little to no documentation and no active support, extending it would have been a difficult and time-consuming process. Moreover, it is much heavier and harder to extend compared to FastAPI. Considering these factors, we decided to go with FastAPI for its lightweight nature and ease of use.",{"type":23,"tag":2766,"props":2777,"children":2778},{},[],{"type":29,"value":2780},"With FastAPI, we were able to quickly develop the necessary API for the iOS developer to utilize, streamlining the development process and ensuring a more efficient and maintainable solution, using a modern framework. This will also allow for the client to opt into creating new clients for web and Android, as having an API gives them that option.",{"type":23,"tag":32,"props":2782,"children":2783},{},[2784],{"type":29,"value":2785},"These real-world use cases demonstrate FastAPI's capabilities in handling a variety of projects across different industries. FastAPI's performance, ease of use, and data validation features make it an attractive choice for companies and organizations looking to build efficient, reliable, and high-performance web applications and APIs.",{"type":23,"tag":24,"props":2787,"children":2789},{"id":2788},"key-takeaways",[2790],{"type":29,"value":2791},"Key Takeaways",{"type":23,"tag":32,"props":2793,"children":2794},{},[2795],{"type":29,"value":2796},"Here are some of the key highlights of FastAPI as a Python web framework:",{"type":23,"tag":2639,"props":2798,"children":2799},{},[2800,2805,2810,2815,2820],{"type":23,"tag":2643,"props":2801,"children":2802},{},[2803],{"type":29,"value":2804},"Excellent performance thanks to optimizations like ASGI support",{"type":23,"tag":2643,"props":2806,"children":2807},{},[2808],{"type":29,"value":2809},"Automatic data validation using Pydantic and type hints",{"type":23,"tag":2643,"props":2811,"children":2812},{},[2813],{"type":29,"value":2814},"Streamlined API development experience",{"type":23,"tag":2643,"props":2816,"children":2817},{},[2818],{"type":29,"value":2819},"Automatic interactive documentation using OpenAPI schemas",{"type":23,"tag":2643,"props":2821,"children":2822},{},[2823],{"type":29,"value":2824},"Rapidly growing ecosystem of plugins, extensions, and community support",{"type":23,"tag":32,"props":2826,"children":2827},{},[2828],{"type":29,"value":2829},"FastAPI is ideal for developers who prioritize building APIs and web services with high productivity and little boilerplate code. Its balance of ease-of-use, flexibility, performance, and reliability make it a worthy contender among Python's web frameworks.",{"type":23,"tag":32,"props":2831,"children":2832},{},[2833],{"type":29,"value":2834},"As FastAPI continues maturing, it will be exciting to see the additional capabilities it develops alongside the growing community support. Based on my experience so far, I believe FastAPI can be the right choice for many API and web app projects that need speed, validation, and great documentation.",{"type":23,"tag":2836,"props":2837,"children":2838},"style",{},[2839],{"type":29,"value":2840},"html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}",{"title":8,"searchDepth":190,"depth":190,"links":2842},[2843,2844,2851,2857,2866,2867,2868],{"id":26,"depth":180,"text":30},{"id":107,"depth":180,"text":110,"children":2845},[2846,2847,2848,2849,2850],{"id":119,"depth":190,"text":122},{"id":389,"depth":190,"text":392},{"id":435,"depth":190,"text":438},{"id":569,"depth":190,"text":572},{"id":595,"depth":190,"text":598},{"id":617,"depth":180,"text":620,"children":2852},[2853,2854,2855,2856],{"id":628,"depth":190,"text":631},{"id":672,"depth":190,"text":675},{"id":1615,"depth":190,"text":1618},{"id":2251,"depth":190,"text":2254},{"id":2532,"depth":180,"text":2535,"children":2858},[2859,2860,2861,2862,2863,2864,2865],{"id":2543,"depth":190,"text":2546},{"id":2554,"depth":190,"text":2557},{"id":2565,"depth":190,"text":2568},{"id":2576,"depth":190,"text":2579},{"id":2587,"depth":190,"text":2590},{"id":2607,"depth":190,"text":2610},{"id":2618,"depth":190,"text":2621},{"id":2629,"depth":180,"text":2632},{"id":2696,"depth":180,"text":2699},{"id":2788,"depth":180,"text":2791},"markdown","content:jestep:2023-3:fastapi.md","content","jestep/2023-3/fastapi.md","jestep/2023-3/fastapi","md",{"user":2876,"name":2877},"jestep","Jagger Estep",{"_path":2879,"_dir":2880,"_draft":7,"_partial":7,"_locale":8,"title":2881,"description":2882,"publishDate":2883,"tags":2884,"excerpt":2882,"body":2886,"_type":2869,"_id":5519,"_source":2871,"_file":5520,"_stem":5521,"_extension":2874,"author":5522},"/ckeefer/2016-4/djangochannels2","2016-4","Django Channels: From the Ground Up - Part 2","Last time, we decided to embark on a brave new adventure and give our Django framework a big upgrade with the inclusion of Django Channels. We got just far enough to get the development server running, but while this may be an adequate start, it's better to develop against something like what we intend to deploy, right?","2016-06-15",[14,12,2885],"websockets",{"type":20,"children":2887,"toc":5510},[2888,2915,2920,2926,2931,2939,3140,3148,3310,3330,3336,3341,3346,3425,3430,3436,3447,3452,3457,3462,3602,3733,3746,3812,3817,3838,3844,3849,3870,3876,3889,3903,3924,4106,4111,4254,4259,4280,4285,4486,4506,4526,4540,4710,4731,4736,4833,4846,4866,4871,4877,4889,4894,4915,5426,5433,5438,5458,5463,5469,5481,5493,5506],{"type":23,"tag":32,"props":2889,"children":2890},{},[2891,2897,2899,2906,2908,2913],{"type":23,"tag":36,"props":2892,"children":2894},{"href":2893},"/search/user:ckeefer/django/channels/",[2895],{"type":29,"value":2896},"Last time",{"type":29,"value":2898},", we decided to embark on a brave new adventure and give our Django framework a big upgrade with the inclusion of ",{"type":23,"tag":36,"props":2900,"children":2903},{"href":2901,"rel":2902},"https://channels.readthedocs.io/en/latest/index.html",[40],[2904],{"type":29,"value":2905},"Django Channels",{"type":29,"value":2907},". We got just far enough to get the development server running, but while this may be an ",{"type":23,"tag":428,"props":2909,"children":2910},{},[2911],{"type":29,"value":2912},"adequate",{"type":29,"value":2914}," start, it's better to develop against something like what we intend to deploy, right?",{"type":23,"tag":32,"props":2916,"children":2917},{},[2918],{"type":29,"value":2919},"So, let's go the rest of the way and get ready to develop against something that at least resembles a standard production-ready environment with Django Channels.",{"type":23,"tag":117,"props":2921,"children":2923},{"id":2922},"post-receive-redux",[2924],{"type":29,"value":2925},"Post-Receive Redux",{"type":23,"tag":32,"props":2927,"children":2928},{},[2929],{"type":29,"value":2930},"Since we're transitioning away from the Django development server, we'll want to alter our post-receive scripts to tie into supervisorctl instead. So, let's edit our post-receive script like so:",{"type":23,"tag":32,"props":2932,"children":2933},{},[2934],{"type":23,"tag":2647,"props":2935,"children":2936},{},[2937],{"type":29,"value":2938},"Ubuntu (bash) example:",{"type":23,"tag":141,"props":2940,"children":2944},{"className":2941,"code":2942,"language":2943,"meta":8,"style":8},"language-bash shiki shiki-themes github-light github-dark","#!/bin/bash\n\nGIT_WORK_TREE=/home/web/www/ git checkout -f\n\nsource /home/web/venv/bin/activate\npushd /home/web/www/\n\n# Install python libs via pip and perform database migrations\npip install --upgrade -r requirements.txt\npython manage.py migrate\n\npopd\ndeactivate\nsupervisorctl restart server_workers\nsupervisorctl restart server_interface\n","bash",[2945],{"type":23,"tag":147,"props":2946,"children":2947},{"__ignoreMap":8},[2948,2956,2963,2995,3002,3015,3023,3030,3038,3066,3083,3090,3098,3106,3124],{"type":23,"tag":151,"props":2949,"children":2950},{"class":153,"line":154},[2951],{"type":23,"tag":151,"props":2952,"children":2953},{"style":324},[2954],{"type":29,"value":2955},"#!/bin/bash\n",{"type":23,"tag":151,"props":2957,"children":2958},{"class":153,"line":180},[2959],{"type":23,"tag":151,"props":2960,"children":2961},{"emptyLinePlaceholder":184},[2962],{"type":29,"value":187},{"type":23,"tag":151,"props":2964,"children":2965},{"class":153,"line":190},[2966,2971,2975,2980,2985,2990],{"type":23,"tag":151,"props":2967,"children":2968},{"style":164},[2969],{"type":29,"value":2970},"GIT_WORK_TREE",{"type":23,"tag":151,"props":2972,"children":2973},{"style":158},[2974],{"type":29,"value":544},{"type":23,"tag":151,"props":2976,"children":2977},{"style":285},[2978],{"type":29,"value":2979},"/home/web/www/",{"type":23,"tag":151,"props":2981,"children":2982},{"style":199},[2983],{"type":29,"value":2984}," git",{"type":23,"tag":151,"props":2986,"children":2987},{"style":285},[2988],{"type":29,"value":2989}," checkout",{"type":23,"tag":151,"props":2991,"children":2992},{"style":224},[2993],{"type":29,"value":2994}," -f\n",{"type":23,"tag":151,"props":2996,"children":2997},{"class":153,"line":220},[2998],{"type":23,"tag":151,"props":2999,"children":3000},{"emptyLinePlaceholder":184},[3001],{"type":29,"value":187},{"type":23,"tag":151,"props":3003,"children":3004},{"class":153,"line":240},[3005,3010],{"type":23,"tag":151,"props":3006,"children":3007},{"style":224},[3008],{"type":29,"value":3009},"source",{"type":23,"tag":151,"props":3011,"children":3012},{"style":285},[3013],{"type":29,"value":3014}," /home/web/venv/bin/activate\n",{"type":23,"tag":151,"props":3016,"children":3017},{"class":153,"line":254},[3018],{"type":23,"tag":151,"props":3019,"children":3020},{"style":164},[3021],{"type":29,"value":3022},"pushd /home/web/www/\n",{"type":23,"tag":151,"props":3024,"children":3025},{"class":153,"line":263},[3026],{"type":23,"tag":151,"props":3027,"children":3028},{"emptyLinePlaceholder":184},[3029],{"type":29,"value":187},{"type":23,"tag":151,"props":3031,"children":3032},{"class":153,"line":272},[3033],{"type":23,"tag":151,"props":3034,"children":3035},{"style":324},[3036],{"type":29,"value":3037},"# Install python libs via pip and perform database migrations\n",{"type":23,"tag":151,"props":3039,"children":3040},{"class":153,"line":296},[3041,3046,3051,3056,3061],{"type":23,"tag":151,"props":3042,"children":3043},{"style":199},[3044],{"type":29,"value":3045},"pip",{"type":23,"tag":151,"props":3047,"children":3048},{"style":285},[3049],{"type":29,"value":3050}," install",{"type":23,"tag":151,"props":3052,"children":3053},{"style":224},[3054],{"type":29,"value":3055}," --upgrade",{"type":23,"tag":151,"props":3057,"children":3058},{"style":224},[3059],{"type":29,"value":3060}," -r",{"type":23,"tag":151,"props":3062,"children":3063},{"style":285},[3064],{"type":29,"value":3065}," requirements.txt\n",{"type":23,"tag":151,"props":3067,"children":3068},{"class":153,"line":320},[3069,3073,3078],{"type":23,"tag":151,"props":3070,"children":3071},{"style":199},[3072],{"type":29,"value":12},{"type":23,"tag":151,"props":3074,"children":3075},{"style":285},[3076],{"type":29,"value":3077}," manage.py",{"type":23,"tag":151,"props":3079,"children":3080},{"style":285},[3081],{"type":29,"value":3082}," migrate\n",{"type":23,"tag":151,"props":3084,"children":3085},{"class":153,"line":330},[3086],{"type":23,"tag":151,"props":3087,"children":3088},{"emptyLinePlaceholder":184},[3089],{"type":29,"value":187},{"type":23,"tag":151,"props":3091,"children":3092},{"class":153,"line":901},[3093],{"type":23,"tag":151,"props":3094,"children":3095},{"style":164},[3096],{"type":29,"value":3097},"popd\n",{"type":23,"tag":151,"props":3099,"children":3100},{"class":153,"line":922},[3101],{"type":23,"tag":151,"props":3102,"children":3103},{"style":199},[3104],{"type":29,"value":3105},"deactivate\n",{"type":23,"tag":151,"props":3107,"children":3108},{"class":153,"line":930},[3109,3114,3119],{"type":23,"tag":151,"props":3110,"children":3111},{"style":199},[3112],{"type":29,"value":3113},"supervisorctl",{"type":23,"tag":151,"props":3115,"children":3116},{"style":285},[3117],{"type":29,"value":3118}," restart",{"type":23,"tag":151,"props":3120,"children":3121},{"style":285},[3122],{"type":29,"value":3123}," server_workers\n",{"type":23,"tag":151,"props":3125,"children":3126},{"class":153,"line":971},[3127,3131,3135],{"type":23,"tag":151,"props":3128,"children":3129},{"style":199},[3130],{"type":29,"value":3113},{"type":23,"tag":151,"props":3132,"children":3133},{"style":285},[3134],{"type":29,"value":3118},{"type":23,"tag":151,"props":3136,"children":3137},{"style":285},[3138],{"type":29,"value":3139}," server_interface\n",{"type":23,"tag":32,"props":3141,"children":3142},{},[3143],{"type":23,"tag":2647,"props":3144,"children":3145},{},[3146],{"type":29,"value":3147},"FreeBSD (csh) example:",{"type":23,"tag":141,"props":3149,"children":3151},{"className":2941,"code":3150,"language":2943,"meta":8,"style":8},"#!/bin/csh\n\nenv GIT_WORK_TREE=/home/web/www/ git checkout -f\nsource /home/web/venv/bin/activate.csh\npushd /home/web/www/\n\npip install --upgrade -r requirements.txt\npython manage.py migrate\n\npopd\ndeactivate\nsupervisorctl restart server_workers\nsupervisorctl restart server_interface\n",[3152],{"type":23,"tag":147,"props":3153,"children":3154},{"__ignoreMap":8},[3155,3163,3170,3195,3207,3214,3221,3244,3259,3266,3273,3280,3295],{"type":23,"tag":151,"props":3156,"children":3157},{"class":153,"line":154},[3158],{"type":23,"tag":151,"props":3159,"children":3160},{"style":324},[3161],{"type":29,"value":3162},"#!/bin/csh\n",{"type":23,"tag":151,"props":3164,"children":3165},{"class":153,"line":180},[3166],{"type":23,"tag":151,"props":3167,"children":3168},{"emptyLinePlaceholder":184},[3169],{"type":29,"value":187},{"type":23,"tag":151,"props":3171,"children":3172},{"class":153,"line":190},[3173,3178,3183,3187,3191],{"type":23,"tag":151,"props":3174,"children":3175},{"style":199},[3176],{"type":29,"value":3177},"env",{"type":23,"tag":151,"props":3179,"children":3180},{"style":285},[3181],{"type":29,"value":3182}," GIT_WORK_TREE=/home/web/www/",{"type":23,"tag":151,"props":3184,"children":3185},{"style":285},[3186],{"type":29,"value":2984},{"type":23,"tag":151,"props":3188,"children":3189},{"style":285},[3190],{"type":29,"value":2989},{"type":23,"tag":151,"props":3192,"children":3193},{"style":224},[3194],{"type":29,"value":2994},{"type":23,"tag":151,"props":3196,"children":3197},{"class":153,"line":220},[3198,3202],{"type":23,"tag":151,"props":3199,"children":3200},{"style":224},[3201],{"type":29,"value":3009},{"type":23,"tag":151,"props":3203,"children":3204},{"style":285},[3205],{"type":29,"value":3206}," /home/web/venv/bin/activate.csh\n",{"type":23,"tag":151,"props":3208,"children":3209},{"class":153,"line":240},[3210],{"type":23,"tag":151,"props":3211,"children":3212},{"style":164},[3213],{"type":29,"value":3022},{"type":23,"tag":151,"props":3215,"children":3216},{"class":153,"line":254},[3217],{"type":23,"tag":151,"props":3218,"children":3219},{"emptyLinePlaceholder":184},[3220],{"type":29,"value":187},{"type":23,"tag":151,"props":3222,"children":3223},{"class":153,"line":263},[3224,3228,3232,3236,3240],{"type":23,"tag":151,"props":3225,"children":3226},{"style":199},[3227],{"type":29,"value":3045},{"type":23,"tag":151,"props":3229,"children":3230},{"style":285},[3231],{"type":29,"value":3050},{"type":23,"tag":151,"props":3233,"children":3234},{"style":224},[3235],{"type":29,"value":3055},{"type":23,"tag":151,"props":3237,"children":3238},{"style":224},[3239],{"type":29,"value":3060},{"type":23,"tag":151,"props":3241,"children":3242},{"style":285},[3243],{"type":29,"value":3065},{"type":23,"tag":151,"props":3245,"children":3246},{"class":153,"line":272},[3247,3251,3255],{"type":23,"tag":151,"props":3248,"children":3249},{"style":199},[3250],{"type":29,"value":12},{"type":23,"tag":151,"props":3252,"children":3253},{"style":285},[3254],{"type":29,"value":3077},{"type":23,"tag":151,"props":3256,"children":3257},{"style":285},[3258],{"type":29,"value":3082},{"type":23,"tag":151,"props":3260,"children":3261},{"class":153,"line":296},[3262],{"type":23,"tag":151,"props":3263,"children":3264},{"emptyLinePlaceholder":184},[3265],{"type":29,"value":187},{"type":23,"tag":151,"props":3267,"children":3268},{"class":153,"line":320},[3269],{"type":23,"tag":151,"props":3270,"children":3271},{"style":164},[3272],{"type":29,"value":3097},{"type":23,"tag":151,"props":3274,"children":3275},{"class":153,"line":330},[3276],{"type":23,"tag":151,"props":3277,"children":3278},{"style":199},[3279],{"type":29,"value":3105},{"type":23,"tag":151,"props":3281,"children":3282},{"class":153,"line":901},[3283,3287,3291],{"type":23,"tag":151,"props":3284,"children":3285},{"style":199},[3286],{"type":29,"value":3113},{"type":23,"tag":151,"props":3288,"children":3289},{"style":285},[3290],{"type":29,"value":3118},{"type":23,"tag":151,"props":3292,"children":3293},{"style":285},[3294],{"type":29,"value":3123},{"type":23,"tag":151,"props":3296,"children":3297},{"class":153,"line":922},[3298,3302,3306],{"type":23,"tag":151,"props":3299,"children":3300},{"style":199},[3301],{"type":29,"value":3113},{"type":23,"tag":151,"props":3303,"children":3304},{"style":285},[3305],{"type":29,"value":3118},{"type":23,"tag":151,"props":3307,"children":3308},{"style":285},[3309],{"type":29,"value":3139},{"type":23,"tag":32,"props":3311,"children":3312},{},[3313,3315,3321,3322,3328],{"type":29,"value":3314},"We can also work in things like running ",{"type":23,"tag":147,"props":3316,"children":3318},{"className":3317},[],[3319],{"type":29,"value":3320},"npm",{"type":29,"value":68},{"type":23,"tag":147,"props":3323,"children":3325},{"className":3324},[],[3326],{"type":29,"value":3327},"grunt",{"type":29,"value":3329}," in there to build and minify files, or whatever other processes you need to do when pushing new code. This will likely be a little different for every project.",{"type":23,"tag":117,"props":3331,"children":3333},{"id":3332},"postgresql-setup",[3334],{"type":29,"value":3335},"Postgresql Setup",{"type":23,"tag":32,"props":3337,"children":3338},{},[3339],{"type":29,"value":3340},"Now, let's get our main database up and ready for our application. Up to this point, we've been relying on sqlite, which is a good starting place, but no match for a world-class hitter like PostgreSQL.",{"type":23,"tag":32,"props":3342,"children":3343},{},[3344],{"type":29,"value":3345},"We'll su in as the standard postgres user, and pass user and database creation commands to psql:",{"type":23,"tag":141,"props":3347,"children":3349},{"className":2941,"code":3348,"language":2943,"meta":8,"style":8},"vsudo su - postgres \u003C\u003CEOF\necho \"CREATE ROLE web LOGIN ENCRYPTED PASSWORD '';\" | psql\ncreatedb \"your_postgres_db\" --owner \"web\"\necho \"GRANT ALL PRIVILEGES ON DATABASE your_postgres_db TO web\" | psql\nservice postgresql reload\nEOF\n",[3350],{"type":23,"tag":147,"props":3351,"children":3352},{"__ignoreMap":8},[3353,3386,3394,3402,3410,3418],{"type":23,"tag":151,"props":3354,"children":3355},{"class":153,"line":154},[3356,3361,3366,3371,3376,3381],{"type":23,"tag":151,"props":3357,"children":3358},{"style":199},[3359],{"type":29,"value":3360},"vsudo",{"type":23,"tag":151,"props":3362,"children":3363},{"style":285},[3364],{"type":29,"value":3365}," su",{"type":23,"tag":151,"props":3367,"children":3368},{"style":285},[3369],{"type":29,"value":3370}," -",{"type":23,"tag":151,"props":3372,"children":3373},{"style":285},[3374],{"type":29,"value":3375}," postgres",{"type":23,"tag":151,"props":3377,"children":3378},{"style":158},[3379],{"type":29,"value":3380}," \u003C\u003C",{"type":23,"tag":151,"props":3382,"children":3383},{"style":285},[3384],{"type":29,"value":3385},"EOF\n",{"type":23,"tag":151,"props":3387,"children":3388},{"class":153,"line":180},[3389],{"type":23,"tag":151,"props":3390,"children":3391},{"style":285},[3392],{"type":29,"value":3393},"echo \"CREATE ROLE web LOGIN ENCRYPTED PASSWORD '';\" | psql\n",{"type":23,"tag":151,"props":3395,"children":3396},{"class":153,"line":190},[3397],{"type":23,"tag":151,"props":3398,"children":3399},{"style":285},[3400],{"type":29,"value":3401},"createdb \"your_postgres_db\" --owner \"web\"\n",{"type":23,"tag":151,"props":3403,"children":3404},{"class":153,"line":220},[3405],{"type":23,"tag":151,"props":3406,"children":3407},{"style":285},[3408],{"type":29,"value":3409},"echo \"GRANT ALL PRIVILEGES ON DATABASE your_postgres_db TO web\" | psql\n",{"type":23,"tag":151,"props":3411,"children":3412},{"class":153,"line":240},[3413],{"type":23,"tag":151,"props":3414,"children":3415},{"style":285},[3416],{"type":29,"value":3417},"service postgresql reload\n",{"type":23,"tag":151,"props":3419,"children":3420},{"class":153,"line":254},[3421],{"type":23,"tag":151,"props":3422,"children":3423},{"style":285},[3424],{"type":29,"value":3385},{"type":23,"tag":32,"props":3426,"children":3427},{},[3428],{"type":29,"value":3429},"And we should be good to go.",{"type":23,"tag":117,"props":3431,"children":3433},{"id":3432},"setup-supervisord",[3434],{"type":29,"value":3435},"Setup Supervisord",{"type":23,"tag":32,"props":3437,"children":3438},{},[3439,3441,3446],{"type":29,"value":3440},"Okay, we're getting closer - now we're going to get supervisor to not only start our programs when it runs, but allow our regular web user to start and stop the programs we're setting up with",{"type":23,"tag":147,"props":3442,"children":3444},{"className":3443},[],[3445],{"type":29,"value":3113},{"type":29,"value":386},{"type":23,"tag":32,"props":3448,"children":3449},{},[3450],{"type":29,"value":3451},"Supervisord is an important part of the puzzle, as it will keep our server interface and workers up and running. If workers fail or die - which is certainly a possibility - our interface will simply sit and hang, waiting for workers to hand it something to do. Inversely, nothing going in or out of the workers is going to get back to your users without the interface, so it's important we have something monitoring both of them.",{"type":23,"tag":32,"props":3453,"children":3454},{},[3455],{"type":29,"value":3456},"We'll need to open up the supervisord.conf file, or add some new files which will be imported into said conf file - on Ubuntu, for example, we can locate our new conf files at /etc/supervisor/conf.d. On FreeBSD, we might just open up the conf file itself - be careful, the install instruction tell us about /etc/supervisord.conf, but their may also be another conf file at /usr/local/etc/ that overrides it.",{"type":23,"tag":32,"props":3458,"children":3459},{},[3460],{"type":29,"value":3461},"Either way, we're going to be adding two new program entries - one for the server workers (which handle the requests in the background) and one for the server interface (which consumes the requests and responses from clients and workers alike).",{"type":23,"tag":141,"props":3463,"children":3465},{"className":2941,"code":3464,"language":2943,"meta":8,"style":8},"[program:server_workers]\ncommand=/home/web/venv/bin/python /home/web/www/manage.py runworker\ndirectory=/home/web/www/\nuser=web\nautostart=true\nautorestart=true\nredirect_stderr=true\nstopasgroup=true\n",[3466],{"type":23,"tag":147,"props":3467,"children":3468},{"__ignoreMap":8},[3469,3477,3504,3521,3537,3554,3570,3586],{"type":23,"tag":151,"props":3470,"children":3471},{"class":153,"line":154},[3472],{"type":23,"tag":151,"props":3473,"children":3474},{"style":164},[3475],{"type":29,"value":3476},"[program:server_workers]\n",{"type":23,"tag":151,"props":3478,"children":3479},{"class":153,"line":180},[3480,3485,3489,3494,3499],{"type":23,"tag":151,"props":3481,"children":3482},{"style":164},[3483],{"type":29,"value":3484},"command",{"type":23,"tag":151,"props":3486,"children":3487},{"style":158},[3488],{"type":29,"value":544},{"type":23,"tag":151,"props":3490,"children":3491},{"style":285},[3492],{"type":29,"value":3493},"/home/web/venv/bin/python",{"type":23,"tag":151,"props":3495,"children":3496},{"style":199},[3497],{"type":29,"value":3498}," /home/web/www/manage.py",{"type":23,"tag":151,"props":3500,"children":3501},{"style":285},[3502],{"type":29,"value":3503}," runworker\n",{"type":23,"tag":151,"props":3505,"children":3506},{"class":153,"line":190},[3507,3512,3516],{"type":23,"tag":151,"props":3508,"children":3509},{"style":164},[3510],{"type":29,"value":3511},"directory",{"type":23,"tag":151,"props":3513,"children":3514},{"style":158},[3515],{"type":29,"value":544},{"type":23,"tag":151,"props":3517,"children":3518},{"style":285},[3519],{"type":29,"value":3520},"/home/web/www/\n",{"type":23,"tag":151,"props":3522,"children":3523},{"class":153,"line":220},[3524,3528,3532],{"type":23,"tag":151,"props":3525,"children":3526},{"style":164},[3527],{"type":29,"value":352},{"type":23,"tag":151,"props":3529,"children":3530},{"style":158},[3531],{"type":29,"value":544},{"type":23,"tag":151,"props":3533,"children":3534},{"style":285},[3535],{"type":29,"value":3536},"web\n",{"type":23,"tag":151,"props":3538,"children":3539},{"class":153,"line":240},[3540,3545,3549],{"type":23,"tag":151,"props":3541,"children":3542},{"style":164},[3543],{"type":29,"value":3544},"autostart",{"type":23,"tag":151,"props":3546,"children":3547},{"style":158},[3548],{"type":29,"value":544},{"type":23,"tag":151,"props":3550,"children":3551},{"style":285},[3552],{"type":29,"value":3553},"true\n",{"type":23,"tag":151,"props":3555,"children":3556},{"class":153,"line":254},[3557,3562,3566],{"type":23,"tag":151,"props":3558,"children":3559},{"style":164},[3560],{"type":29,"value":3561},"autorestart",{"type":23,"tag":151,"props":3563,"children":3564},{"style":158},[3565],{"type":29,"value":544},{"type":23,"tag":151,"props":3567,"children":3568},{"style":285},[3569],{"type":29,"value":3553},{"type":23,"tag":151,"props":3571,"children":3572},{"class":153,"line":263},[3573,3578,3582],{"type":23,"tag":151,"props":3574,"children":3575},{"style":164},[3576],{"type":29,"value":3577},"redirect_stderr",{"type":23,"tag":151,"props":3579,"children":3580},{"style":158},[3581],{"type":29,"value":544},{"type":23,"tag":151,"props":3583,"children":3584},{"style":285},[3585],{"type":29,"value":3553},{"type":23,"tag":151,"props":3587,"children":3588},{"class":153,"line":272},[3589,3594,3598],{"type":23,"tag":151,"props":3590,"children":3591},{"style":164},[3592],{"type":29,"value":3593},"stopasgroup",{"type":23,"tag":151,"props":3595,"children":3596},{"style":158},[3597],{"type":29,"value":544},{"type":23,"tag":151,"props":3599,"children":3600},{"style":285},[3601],{"type":29,"value":3553},{"type":23,"tag":141,"props":3603,"children":3605},{"className":2941,"code":3604,"language":2943,"meta":8,"style":8},"[program:server_interface]\ncommand=/home/web/venv/bin/daphne -b 127.0.0.1 -p 8000 yourapp.asgi:channel_layer\ndirectory=/home/web/www/\nautostart=true\nautorestart=true\nstopasgroup=true\nuser=web\n",[3606],{"type":23,"tag":147,"props":3607,"children":3608},{"__ignoreMap":8},[3609,3617,3658,3673,3688,3703,3718],{"type":23,"tag":151,"props":3610,"children":3611},{"class":153,"line":154},[3612],{"type":23,"tag":151,"props":3613,"children":3614},{"style":164},[3615],{"type":29,"value":3616},"[program:server_interface]\n",{"type":23,"tag":151,"props":3618,"children":3619},{"class":153,"line":180},[3620,3624,3628,3633,3638,3643,3648,3653],{"type":23,"tag":151,"props":3621,"children":3622},{"style":164},[3623],{"type":29,"value":3484},{"type":23,"tag":151,"props":3625,"children":3626},{"style":158},[3627],{"type":29,"value":544},{"type":23,"tag":151,"props":3629,"children":3630},{"style":285},[3631],{"type":29,"value":3632},"/home/web/venv/bin/daphne",{"type":23,"tag":151,"props":3634,"children":3635},{"style":199},[3636],{"type":29,"value":3637}," -b",{"type":23,"tag":151,"props":3639,"children":3640},{"style":224},[3641],{"type":29,"value":3642}," 127.0.0.1",{"type":23,"tag":151,"props":3644,"children":3645},{"style":224},[3646],{"type":29,"value":3647}," -p",{"type":23,"tag":151,"props":3649,"children":3650},{"style":224},[3651],{"type":29,"value":3652}," 8000",{"type":23,"tag":151,"props":3654,"children":3655},{"style":285},[3656],{"type":29,"value":3657}," yourapp.asgi:channel_layer\n",{"type":23,"tag":151,"props":3659,"children":3660},{"class":153,"line":190},[3661,3665,3669],{"type":23,"tag":151,"props":3662,"children":3663},{"style":164},[3664],{"type":29,"value":3511},{"type":23,"tag":151,"props":3666,"children":3667},{"style":158},[3668],{"type":29,"value":544},{"type":23,"tag":151,"props":3670,"children":3671},{"style":285},[3672],{"type":29,"value":3520},{"type":23,"tag":151,"props":3674,"children":3675},{"class":153,"line":220},[3676,3680,3684],{"type":23,"tag":151,"props":3677,"children":3678},{"style":164},[3679],{"type":29,"value":3544},{"type":23,"tag":151,"props":3681,"children":3682},{"style":158},[3683],{"type":29,"value":544},{"type":23,"tag":151,"props":3685,"children":3686},{"style":285},[3687],{"type":29,"value":3553},{"type":23,"tag":151,"props":3689,"children":3690},{"class":153,"line":240},[3691,3695,3699],{"type":23,"tag":151,"props":3692,"children":3693},{"style":164},[3694],{"type":29,"value":3561},{"type":23,"tag":151,"props":3696,"children":3697},{"style":158},[3698],{"type":29,"value":544},{"type":23,"tag":151,"props":3700,"children":3701},{"style":285},[3702],{"type":29,"value":3553},{"type":23,"tag":151,"props":3704,"children":3705},{"class":153,"line":254},[3706,3710,3714],{"type":23,"tag":151,"props":3707,"children":3708},{"style":164},[3709],{"type":29,"value":3593},{"type":23,"tag":151,"props":3711,"children":3712},{"style":158},[3713],{"type":29,"value":544},{"type":23,"tag":151,"props":3715,"children":3716},{"style":285},[3717],{"type":29,"value":3553},{"type":23,"tag":151,"props":3719,"children":3720},{"class":153,"line":263},[3721,3725,3729],{"type":23,"tag":151,"props":3722,"children":3723},{"style":164},[3724],{"type":29,"value":352},{"type":23,"tag":151,"props":3726,"children":3727},{"style":158},[3728],{"type":29,"value":544},{"type":23,"tag":151,"props":3730,"children":3731},{"style":285},[3732],{"type":29,"value":3536},{"type":23,"tag":32,"props":3734,"children":3735},{},[3736,3738,3744],{"type":29,"value":3737},"And finally, up near the top of the supervisord.conf file, beneath the ",{"type":23,"tag":147,"props":3739,"children":3741},{"className":3740},[],[3742],{"type":29,"value":3743},"[unix_http_server]",{"type":29,"value":3745}," heading, we need to make a few small changes to allow our web user to start and stop these processes.",{"type":23,"tag":141,"props":3747,"children":3749},{"className":2941,"code":3748,"language":2943,"meta":8,"style":8},"[unix_http_server]\nfile=/var/run/supervisor/supervisor.sock\nchmod=0770\nchown=nobody:web\n",[3750],{"type":23,"tag":147,"props":3751,"children":3752},{"__ignoreMap":8},[3753,3761,3778,3795],{"type":23,"tag":151,"props":3754,"children":3755},{"class":153,"line":154},[3756],{"type":23,"tag":151,"props":3757,"children":3758},{"style":164},[3759],{"type":29,"value":3760},"[unix_http_server]\n",{"type":23,"tag":151,"props":3762,"children":3763},{"class":153,"line":180},[3764,3769,3773],{"type":23,"tag":151,"props":3765,"children":3766},{"style":164},[3767],{"type":29,"value":3768},"file",{"type":23,"tag":151,"props":3770,"children":3771},{"style":158},[3772],{"type":29,"value":544},{"type":23,"tag":151,"props":3774,"children":3775},{"style":285},[3776],{"type":29,"value":3777},"/var/run/supervisor/supervisor.sock\n",{"type":23,"tag":151,"props":3779,"children":3780},{"class":153,"line":190},[3781,3786,3790],{"type":23,"tag":151,"props":3782,"children":3783},{"style":164},[3784],{"type":29,"value":3785},"chmod",{"type":23,"tag":151,"props":3787,"children":3788},{"style":158},[3789],{"type":29,"value":544},{"type":23,"tag":151,"props":3791,"children":3792},{"style":285},[3793],{"type":29,"value":3794},"0770\n",{"type":23,"tag":151,"props":3796,"children":3797},{"class":153,"line":220},[3798,3803,3807],{"type":23,"tag":151,"props":3799,"children":3800},{"style":164},[3801],{"type":29,"value":3802},"chown",{"type":23,"tag":151,"props":3804,"children":3805},{"style":158},[3806],{"type":29,"value":544},{"type":23,"tag":151,"props":3808,"children":3809},{"style":285},[3810],{"type":29,"value":3811},"nobody:web\n",{"type":23,"tag":32,"props":3813,"children":3814},{},[3815],{"type":29,"value":3816},"and restart the supervisord process.",{"type":23,"tag":32,"props":3818,"children":3819},{},[3820,3822,3828,3830,3836],{"type":29,"value":3821},"You can also opt to add your web user to a group like ",{"type":23,"tag":147,"props":3823,"children":3825},{"className":3824},[],[3826],{"type":29,"value":3827},"wheel",{"type":29,"value":3829},", and then alter the chown line to something like ",{"type":23,"tag":147,"props":3831,"children":3833},{"className":3832},[],[3834],{"type":29,"value":3835},"chown=nobody:wheel",{"type":29,"value":3837},". The key here is simply to make the socket available to the appropriate user group so our web user can interact with it.",{"type":23,"tag":117,"props":3839,"children":3841},{"id":3840},"setup-redis",[3842],{"type":29,"value":3843},"Setup Redis",{"type":23,"tag":32,"props":3845,"children":3846},{},[3847],{"type":29,"value":3848},"Getting Redis up and ready is smooth and easy - in fact, at this point you're already done. See how easy that was?",{"type":23,"tag":32,"props":3850,"children":3851},{},[3852,3854,3860,3862,3868],{"type":29,"value":3853},"If you used the requirements.txt in part 1, you already have the needed ",{"type":23,"tag":147,"props":3855,"children":3857},{"className":3856},[],[3858],{"type":29,"value":3859},"asgi_redis",{"type":29,"value":3861}," package installed in your virtual environment, and the defaults that Redis installs with are fine for us. I do encourage you to take a look through the ",{"type":23,"tag":36,"props":3863,"children":3865},{"href":3864},"redis.io/documentation",[3866],{"type":29,"value":3867},"Redis docs",{"type":29,"value":3869},", however, and configure it as you need.",{"type":23,"tag":117,"props":3871,"children":3873},{"id":3872},"django-setup",[3874],{"type":29,"value":3875},"Django Setup",{"type":23,"tag":32,"props":3877,"children":3878},{},[3879,3881,3887],{"type":29,"value":3880},"If we were to check our supervisord status with ",{"type":23,"tag":147,"props":3882,"children":3884},{"className":3883},[],[3885],{"type":29,"value":3886},"supervisorctl status",{"type":29,"value":3888}," right now, we can expect it will be showing a FATAL error for our server_interface program entry. We need to add some new files to get daphne up and running, and while we're in there we'll make the changes necessary to get Django playing nicely with Postgres as our database and Redis as our channel backend.",{"type":23,"tag":32,"props":3890,"children":3891},{},[3892,3894,3901],{"type":29,"value":3893},"For reference, much of the below can also be found in the ",{"type":23,"tag":36,"props":3895,"children":3898},{"href":3896,"rel":3897},"https://channels.readthedocs.io/en/latest/deploying.html#setting-up-a-channel-backend",[40],[3899],{"type":29,"value":3900},"Channels docs",{"type":29,"value":3902},", with the addition of our postgres details.",{"type":23,"tag":32,"props":3904,"children":3905},{},[3906,3908,3914,3916,3922],{"type":29,"value":3907},"Let's start with PostgreSQL. Open up your ",{"type":23,"tag":147,"props":3909,"children":3911},{"className":3910},[],[3912],{"type":29,"value":3913},"settings.py",{"type":29,"value":3915}," file, and alter the ",{"type":23,"tag":147,"props":3917,"children":3919},{"className":3918},[],[3920],{"type":29,"value":3921},"DATABASES",{"type":29,"value":3923}," dict to the following:",{"type":23,"tag":141,"props":3925,"children":3927},{"className":143,"code":3926,"language":12,"meta":8,"style":8},"DATABASES = {\n'default': {\n'ENGINE': 'django.db.backends.postgresql_psycopg2',\n'NAME': 'your_postgres_db',\n'USER': 'web',\n'PASSWORD': 'your_postgres_pass',\n'HOST': 'localhost',\n'PORT': '', # Empty string == default (5432)\n}\n}\n",[3928],{"type":23,"tag":147,"props":3929,"children":3930},{"__ignoreMap":8},[3931,3947,3960,3981,4002,4023,4044,4065,4091,4099],{"type":23,"tag":151,"props":3932,"children":3933},{"class":153,"line":154},[3934,3938,3942],{"type":23,"tag":151,"props":3935,"children":3936},{"style":224},[3937],{"type":29,"value":3921},{"type":23,"tag":151,"props":3939,"children":3940},{"style":158},[3941],{"type":29,"value":1753},{"type":23,"tag":151,"props":3943,"children":3944},{"style":164},[3945],{"type":29,"value":3946}," {\n",{"type":23,"tag":151,"props":3948,"children":3949},{"class":153,"line":180},[3950,3955],{"type":23,"tag":151,"props":3951,"children":3952},{"style":285},[3953],{"type":29,"value":3954},"'default'",{"type":23,"tag":151,"props":3956,"children":3957},{"style":164},[3958],{"type":29,"value":3959},": {\n",{"type":23,"tag":151,"props":3961,"children":3962},{"class":153,"line":190},[3963,3968,3972,3977],{"type":23,"tag":151,"props":3964,"children":3965},{"style":285},[3966],{"type":29,"value":3967},"'ENGINE'",{"type":23,"tag":151,"props":3969,"children":3970},{"style":164},[3971],{"type":29,"value":232},{"type":23,"tag":151,"props":3973,"children":3974},{"style":285},[3975],{"type":29,"value":3976},"'django.db.backends.postgresql_psycopg2'",{"type":23,"tag":151,"props":3978,"children":3979},{"style":164},[3980],{"type":29,"value":1805},{"type":23,"tag":151,"props":3982,"children":3983},{"class":153,"line":220},[3984,3989,3993,3998],{"type":23,"tag":151,"props":3985,"children":3986},{"style":285},[3987],{"type":29,"value":3988},"'NAME'",{"type":23,"tag":151,"props":3990,"children":3991},{"style":164},[3992],{"type":29,"value":232},{"type":23,"tag":151,"props":3994,"children":3995},{"style":285},[3996],{"type":29,"value":3997},"'your_postgres_db'",{"type":23,"tag":151,"props":3999,"children":4000},{"style":164},[4001],{"type":29,"value":1805},{"type":23,"tag":151,"props":4003,"children":4004},{"class":153,"line":240},[4005,4010,4014,4019],{"type":23,"tag":151,"props":4006,"children":4007},{"style":285},[4008],{"type":29,"value":4009},"'USER'",{"type":23,"tag":151,"props":4011,"children":4012},{"style":164},[4013],{"type":29,"value":232},{"type":23,"tag":151,"props":4015,"children":4016},{"style":285},[4017],{"type":29,"value":4018},"'web'",{"type":23,"tag":151,"props":4020,"children":4021},{"style":164},[4022],{"type":29,"value":1805},{"type":23,"tag":151,"props":4024,"children":4025},{"class":153,"line":254},[4026,4031,4035,4040],{"type":23,"tag":151,"props":4027,"children":4028},{"style":285},[4029],{"type":29,"value":4030},"'PASSWORD'",{"type":23,"tag":151,"props":4032,"children":4033},{"style":164},[4034],{"type":29,"value":232},{"type":23,"tag":151,"props":4036,"children":4037},{"style":285},[4038],{"type":29,"value":4039},"'your_postgres_pass'",{"type":23,"tag":151,"props":4041,"children":4042},{"style":164},[4043],{"type":29,"value":1805},{"type":23,"tag":151,"props":4045,"children":4046},{"class":153,"line":263},[4047,4052,4056,4061],{"type":23,"tag":151,"props":4048,"children":4049},{"style":285},[4050],{"type":29,"value":4051},"'HOST'",{"type":23,"tag":151,"props":4053,"children":4054},{"style":164},[4055],{"type":29,"value":232},{"type":23,"tag":151,"props":4057,"children":4058},{"style":285},[4059],{"type":29,"value":4060},"'localhost'",{"type":23,"tag":151,"props":4062,"children":4063},{"style":164},[4064],{"type":29,"value":1805},{"type":23,"tag":151,"props":4066,"children":4067},{"class":153,"line":272},[4068,4073,4077,4082,4086],{"type":23,"tag":151,"props":4069,"children":4070},{"style":285},[4071],{"type":29,"value":4072},"'PORT'",{"type":23,"tag":151,"props":4074,"children":4075},{"style":164},[4076],{"type":29,"value":232},{"type":23,"tag":151,"props":4078,"children":4079},{"style":285},[4080],{"type":29,"value":4081},"''",{"type":23,"tag":151,"props":4083,"children":4084},{"style":164},[4085],{"type":29,"value":370},{"type":23,"tag":151,"props":4087,"children":4088},{"style":324},[4089],{"type":29,"value":4090},"# Empty string == default (5432)\n",{"type":23,"tag":151,"props":4092,"children":4093},{"class":153,"line":296},[4094],{"type":23,"tag":151,"props":4095,"children":4096},{"style":164},[4097],{"type":29,"value":4098},"}\n",{"type":23,"tag":151,"props":4100,"children":4101},{"class":153,"line":320},[4102],{"type":23,"tag":151,"props":4103,"children":4104},{"style":164},[4105],{"type":29,"value":4098},{"type":23,"tag":32,"props":4107,"children":4108},{},[4109],{"type":29,"value":4110},"Now, add the following dict beneath that to setup our redis channel layer:",{"type":23,"tag":141,"props":4112,"children":4114},{"className":143,"code":4113,"language":12,"meta":8,"style":8},"CHANNEL_LAYERS = {\n\"default\": {\n\"BACKEND\": \"asgi_redis.RedisChannelLayer\",\n\"CONFIG\": {\n\"hosts\": [(\"localhost\", 6379)],\n},\n\"ROUTING\": \"yourapp.routing.channel_routing\",\n},\n}\n",[4115],{"type":23,"tag":147,"props":4116,"children":4117},{"__ignoreMap":8},[4118,4134,4146,4167,4179,4211,4219,4240,4247],{"type":23,"tag":151,"props":4119,"children":4120},{"class":153,"line":154},[4121,4126,4130],{"type":23,"tag":151,"props":4122,"children":4123},{"style":224},[4124],{"type":29,"value":4125},"CHANNEL_LAYERS",{"type":23,"tag":151,"props":4127,"children":4128},{"style":158},[4129],{"type":29,"value":1753},{"type":23,"tag":151,"props":4131,"children":4132},{"style":164},[4133],{"type":29,"value":3946},{"type":23,"tag":151,"props":4135,"children":4136},{"class":153,"line":180},[4137,4142],{"type":23,"tag":151,"props":4138,"children":4139},{"style":285},[4140],{"type":29,"value":4141},"\"default\"",{"type":23,"tag":151,"props":4143,"children":4144},{"style":164},[4145],{"type":29,"value":3959},{"type":23,"tag":151,"props":4147,"children":4148},{"class":153,"line":190},[4149,4154,4158,4163],{"type":23,"tag":151,"props":4150,"children":4151},{"style":285},[4152],{"type":29,"value":4153},"\"BACKEND\"",{"type":23,"tag":151,"props":4155,"children":4156},{"style":164},[4157],{"type":29,"value":232},{"type":23,"tag":151,"props":4159,"children":4160},{"style":285},[4161],{"type":29,"value":4162},"\"asgi_redis.RedisChannelLayer\"",{"type":23,"tag":151,"props":4164,"children":4165},{"style":164},[4166],{"type":29,"value":1805},{"type":23,"tag":151,"props":4168,"children":4169},{"class":153,"line":220},[4170,4175],{"type":23,"tag":151,"props":4171,"children":4172},{"style":285},[4173],{"type":29,"value":4174},"\"CONFIG\"",{"type":23,"tag":151,"props":4176,"children":4177},{"style":164},[4178],{"type":29,"value":3959},{"type":23,"tag":151,"props":4180,"children":4181},{"class":153,"line":240},[4182,4187,4192,4197,4201,4206],{"type":23,"tag":151,"props":4183,"children":4184},{"style":285},[4185],{"type":29,"value":4186},"\"hosts\"",{"type":23,"tag":151,"props":4188,"children":4189},{"style":164},[4190],{"type":29,"value":4191},": [(",{"type":23,"tag":151,"props":4193,"children":4194},{"style":285},[4195],{"type":29,"value":4196},"\"localhost\"",{"type":23,"tag":151,"props":4198,"children":4199},{"style":164},[4200],{"type":29,"value":370},{"type":23,"tag":151,"props":4202,"children":4203},{"style":224},[4204],{"type":29,"value":4205},"6379",{"type":23,"tag":151,"props":4207,"children":4208},{"style":164},[4209],{"type":29,"value":4210},")],\n",{"type":23,"tag":151,"props":4212,"children":4213},{"class":153,"line":254},[4214],{"type":23,"tag":151,"props":4215,"children":4216},{"style":164},[4217],{"type":29,"value":4218},"},\n",{"type":23,"tag":151,"props":4220,"children":4221},{"class":153,"line":263},[4222,4227,4231,4236],{"type":23,"tag":151,"props":4223,"children":4224},{"style":285},[4225],{"type":29,"value":4226},"\"ROUTING\"",{"type":23,"tag":151,"props":4228,"children":4229},{"style":164},[4230],{"type":29,"value":232},{"type":23,"tag":151,"props":4232,"children":4233},{"style":285},[4234],{"type":29,"value":4235},"\"yourapp.routing.channel_routing\"",{"type":23,"tag":151,"props":4237,"children":4238},{"style":164},[4239],{"type":29,"value":1805},{"type":23,"tag":151,"props":4241,"children":4242},{"class":153,"line":272},[4243],{"type":23,"tag":151,"props":4244,"children":4245},{"style":164},[4246],{"type":29,"value":4218},{"type":23,"tag":151,"props":4248,"children":4249},{"class":153,"line":296},[4250],{"type":23,"tag":151,"props":4251,"children":4252},{"style":164},[4253],{"type":29,"value":4098},{"type":23,"tag":32,"props":4255,"children":4256},{},[4257],{"type":29,"value":4258},"By default, channels uses process memory as it's communication channel, which is fine for simple development cases, but obviously can't be shared between processes and is unsuitable for production.",{"type":23,"tag":32,"props":4260,"children":4261},{},[4262,4264,4270,4272,4278],{"type":29,"value":4263},"Notice the \"ROUTING\" component of the above config? That's a reference to the ",{"type":23,"tag":147,"props":4265,"children":4267},{"className":4266},[],[4268],{"type":29,"value":4269},"routing.py",{"type":29,"value":4271}," file we're going to slap into the application directory - it should live next to your ",{"type":23,"tag":147,"props":4273,"children":4275},{"className":4274},[],[4276],{"type":29,"value":4277},"urls.py",{"type":29,"value":4279}," file.",{"type":23,"tag":32,"props":4281,"children":4282},{},[4283],{"type":29,"value":4284},"Here's an example:",{"type":23,"tag":141,"props":4286,"children":4288},{"className":143,"code":4287,"language":12,"meta":8,"style":8},"from channels.routing import route\nfrom channels.routing import include\n\nfrom yourapp import consumers\n\nhttp_routing = [\nroute('http.request', consumers.index)\n]\n\nstream_routing = [\n\n]\n\nchannel_routing = [\ninclude(stream_routing)\n]\n",[4289],{"type":23,"tag":147,"props":4290,"children":4291},{"__ignoreMap":8},[4292,4313,4333,4340,4361,4368,4385,4403,4411,4418,4434,4441,4448,4455,4471,4479],{"type":23,"tag":151,"props":4293,"children":4294},{"class":153,"line":154},[4295,4299,4304,4308],{"type":23,"tag":151,"props":4296,"children":4297},{"style":158},[4298],{"type":29,"value":161},{"type":23,"tag":151,"props":4300,"children":4301},{"style":164},[4302],{"type":29,"value":4303}," channels.routing ",{"type":23,"tag":151,"props":4305,"children":4306},{"style":158},[4307],{"type":29,"value":172},{"type":23,"tag":151,"props":4309,"children":4310},{"style":164},[4311],{"type":29,"value":4312}," route\n",{"type":23,"tag":151,"props":4314,"children":4315},{"class":153,"line":180},[4316,4320,4324,4328],{"type":23,"tag":151,"props":4317,"children":4318},{"style":158},[4319],{"type":29,"value":161},{"type":23,"tag":151,"props":4321,"children":4322},{"style":164},[4323],{"type":29,"value":4303},{"type":23,"tag":151,"props":4325,"children":4326},{"style":158},[4327],{"type":29,"value":172},{"type":23,"tag":151,"props":4329,"children":4330},{"style":164},[4331],{"type":29,"value":4332}," include\n",{"type":23,"tag":151,"props":4334,"children":4335},{"class":153,"line":190},[4336],{"type":23,"tag":151,"props":4337,"children":4338},{"emptyLinePlaceholder":184},[4339],{"type":29,"value":187},{"type":23,"tag":151,"props":4341,"children":4342},{"class":153,"line":220},[4343,4347,4352,4356],{"type":23,"tag":151,"props":4344,"children":4345},{"style":158},[4346],{"type":29,"value":161},{"type":23,"tag":151,"props":4348,"children":4349},{"style":164},[4350],{"type":29,"value":4351}," yourapp ",{"type":23,"tag":151,"props":4353,"children":4354},{"style":158},[4355],{"type":29,"value":172},{"type":23,"tag":151,"props":4357,"children":4358},{"style":164},[4359],{"type":29,"value":4360}," consumers\n",{"type":23,"tag":151,"props":4362,"children":4363},{"class":153,"line":240},[4364],{"type":23,"tag":151,"props":4365,"children":4366},{"emptyLinePlaceholder":184},[4367],{"type":29,"value":187},{"type":23,"tag":151,"props":4369,"children":4370},{"class":153,"line":254},[4371,4376,4380],{"type":23,"tag":151,"props":4372,"children":4373},{"style":164},[4374],{"type":29,"value":4375},"http_routing ",{"type":23,"tag":151,"props":4377,"children":4378},{"style":158},[4379],{"type":29,"value":544},{"type":23,"tag":151,"props":4381,"children":4382},{"style":164},[4383],{"type":29,"value":4384}," [\n",{"type":23,"tag":151,"props":4386,"children":4387},{"class":153,"line":263},[4388,4393,4398],{"type":23,"tag":151,"props":4389,"children":4390},{"style":164},[4391],{"type":29,"value":4392},"route(",{"type":23,"tag":151,"props":4394,"children":4395},{"style":285},[4396],{"type":29,"value":4397},"'http.request'",{"type":23,"tag":151,"props":4399,"children":4400},{"style":164},[4401],{"type":29,"value":4402},", consumers.index)\n",{"type":23,"tag":151,"props":4404,"children":4405},{"class":153,"line":272},[4406],{"type":23,"tag":151,"props":4407,"children":4408},{"style":164},[4409],{"type":29,"value":4410},"]\n",{"type":23,"tag":151,"props":4412,"children":4413},{"class":153,"line":296},[4414],{"type":23,"tag":151,"props":4415,"children":4416},{"emptyLinePlaceholder":184},[4417],{"type":29,"value":187},{"type":23,"tag":151,"props":4419,"children":4420},{"class":153,"line":320},[4421,4426,4430],{"type":23,"tag":151,"props":4422,"children":4423},{"style":164},[4424],{"type":29,"value":4425},"stream_routing ",{"type":23,"tag":151,"props":4427,"children":4428},{"style":158},[4429],{"type":29,"value":544},{"type":23,"tag":151,"props":4431,"children":4432},{"style":164},[4433],{"type":29,"value":4384},{"type":23,"tag":151,"props":4435,"children":4436},{"class":153,"line":330},[4437],{"type":23,"tag":151,"props":4438,"children":4439},{"emptyLinePlaceholder":184},[4440],{"type":29,"value":187},{"type":23,"tag":151,"props":4442,"children":4443},{"class":153,"line":901},[4444],{"type":23,"tag":151,"props":4445,"children":4446},{"style":164},[4447],{"type":29,"value":4410},{"type":23,"tag":151,"props":4449,"children":4450},{"class":153,"line":922},[4451],{"type":23,"tag":151,"props":4452,"children":4453},{"emptyLinePlaceholder":184},[4454],{"type":29,"value":187},{"type":23,"tag":151,"props":4456,"children":4457},{"class":153,"line":930},[4458,4463,4467],{"type":23,"tag":151,"props":4459,"children":4460},{"style":164},[4461],{"type":29,"value":4462},"channel_routing ",{"type":23,"tag":151,"props":4464,"children":4465},{"style":158},[4466],{"type":29,"value":544},{"type":23,"tag":151,"props":4468,"children":4469},{"style":164},[4470],{"type":29,"value":4384},{"type":23,"tag":151,"props":4472,"children":4473},{"class":153,"line":971},[4474],{"type":23,"tag":151,"props":4475,"children":4476},{"style":164},[4477],{"type":29,"value":4478},"include(stream_routing)\n",{"type":23,"tag":151,"props":4480,"children":4481},{"class":153,"line":992},[4482],{"type":23,"tag":151,"props":4483,"children":4484},{"style":164},[4485],{"type":29,"value":4410},{"type":23,"tag":32,"props":4487,"children":4488},{},[4489,4491,4496,4498,4504],{"type":29,"value":4490},"We'll be fleshing this file out in Part 3, so stay tuned. In the meantime, you can also add urls as normal to ",{"type":23,"tag":147,"props":4492,"children":4494},{"className":4493},[],[4495],{"type":29,"value":4277},{"type":29,"value":4497},", referencing standard views, either in a ",{"type":23,"tag":147,"props":4499,"children":4501},{"className":4500},[],[4502],{"type":29,"value":4503},"views.py",{"type":29,"value":4505}," file, or in a directory - this is all exactly the same as it is in standard Django, without any need for changes to accommodate the new ASGI server.",{"type":23,"tag":32,"props":4507,"children":4508},{},[4509,4511,4516,4518,4524],{"type":29,"value":4510},"Next to the ",{"type":23,"tag":147,"props":4512,"children":4514},{"className":4513},[],[4515],{"type":29,"value":4503},{"type":29,"value":4517}," file in your application, add a new file named ",{"type":23,"tag":147,"props":4519,"children":4521},{"className":4520},[],[4522],{"type":29,"value":4523},"consumers.py",{"type":29,"value":4525},". These consumers will be the callables handling the routing we'll be setting up in routing.py - these are what will enable something like websocket requests and replies, rather than being tied into the standard Django view structure.",{"type":23,"tag":32,"props":4527,"children":4528},{},[4529,4531,4538],{"type":29,"value":4530},"For now, we'll put in a simple example of handling an http request, straight from the ",{"type":23,"tag":36,"props":4532,"children":4535},{"href":4533,"rel":4534},"https://channels.readthedocs.io/en/latest/getting-started.html#first-consumers",[40],[4536],{"type":29,"value":4537},"channels docs",{"type":29,"value":4539},":",{"type":23,"tag":141,"props":4541,"children":4543},{"className":143,"code":4542,"language":12,"meta":8,"style":8},"from django.http import HttpResponse\nfrom channels.handler import AsgiHandler\n\ndef http_consumer(message):\n# Make standard HTTP response - access ASGI path attribute directly\nresponse = HttpResponse(\"Hello world! You asked for %s\" % message.content['path'])\n# Encode that response into message format (ASGI)\nfor chunk in AsgiHandler.encode_response(response):\nmessage.reply_channel.send(chunk)\n",[4544],{"type":23,"tag":147,"props":4545,"children":4546},{"__ignoreMap":8},[4547,4568,4589,4596,4613,4621,4671,4679,4702],{"type":23,"tag":151,"props":4548,"children":4549},{"class":153,"line":154},[4550,4554,4559,4563],{"type":23,"tag":151,"props":4551,"children":4552},{"style":158},[4553],{"type":29,"value":161},{"type":23,"tag":151,"props":4555,"children":4556},{"style":164},[4557],{"type":29,"value":4558}," django.http ",{"type":23,"tag":151,"props":4560,"children":4561},{"style":158},[4562],{"type":29,"value":172},{"type":23,"tag":151,"props":4564,"children":4565},{"style":164},[4566],{"type":29,"value":4567}," HttpResponse\n",{"type":23,"tag":151,"props":4569,"children":4570},{"class":153,"line":180},[4571,4575,4580,4584],{"type":23,"tag":151,"props":4572,"children":4573},{"style":158},[4574],{"type":29,"value":161},{"type":23,"tag":151,"props":4576,"children":4577},{"style":164},[4578],{"type":29,"value":4579}," channels.handler ",{"type":23,"tag":151,"props":4581,"children":4582},{"style":158},[4583],{"type":29,"value":172},{"type":23,"tag":151,"props":4585,"children":4586},{"style":164},[4587],{"type":29,"value":4588}," AsgiHandler\n",{"type":23,"tag":151,"props":4590,"children":4591},{"class":153,"line":190},[4592],{"type":23,"tag":151,"props":4593,"children":4594},{"emptyLinePlaceholder":184},[4595],{"type":29,"value":187},{"type":23,"tag":151,"props":4597,"children":4598},{"class":153,"line":220},[4599,4603,4608],{"type":23,"tag":151,"props":4600,"children":4601},{"style":158},[4602],{"type":29,"value":794},{"type":23,"tag":151,"props":4604,"children":4605},{"style":199},[4606],{"type":29,"value":4607}," http_consumer",{"type":23,"tag":151,"props":4609,"children":4610},{"style":164},[4611],{"type":29,"value":4612},"(message):\n",{"type":23,"tag":151,"props":4614,"children":4615},{"class":153,"line":240},[4616],{"type":23,"tag":151,"props":4617,"children":4618},{"style":324},[4619],{"type":29,"value":4620},"# Make standard HTTP response - access ASGI path attribute directly\n",{"type":23,"tag":151,"props":4622,"children":4623},{"class":153,"line":254},[4624,4629,4633,4638,4643,4648,4652,4657,4662,4667],{"type":23,"tag":151,"props":4625,"children":4626},{"style":164},[4627],{"type":29,"value":4628},"response ",{"type":23,"tag":151,"props":4630,"children":4631},{"style":158},[4632],{"type":29,"value":544},{"type":23,"tag":151,"props":4634,"children":4635},{"style":164},[4636],{"type":29,"value":4637}," HttpResponse(",{"type":23,"tag":151,"props":4639,"children":4640},{"style":285},[4641],{"type":29,"value":4642},"\"Hello world! You asked for ",{"type":23,"tag":151,"props":4644,"children":4645},{"style":224},[4646],{"type":29,"value":4647},"%s",{"type":23,"tag":151,"props":4649,"children":4650},{"style":285},[4651],{"type":29,"value":497},{"type":23,"tag":151,"props":4653,"children":4654},{"style":158},[4655],{"type":29,"value":4656}," %",{"type":23,"tag":151,"props":4658,"children":4659},{"style":164},[4660],{"type":29,"value":4661}," message.content[",{"type":23,"tag":151,"props":4663,"children":4664},{"style":285},[4665],{"type":29,"value":4666},"'path'",{"type":23,"tag":151,"props":4668,"children":4669},{"style":164},[4670],{"type":29,"value":1963},{"type":23,"tag":151,"props":4672,"children":4673},{"class":153,"line":263},[4674],{"type":23,"tag":151,"props":4675,"children":4676},{"style":324},[4677],{"type":29,"value":4678},"# Encode that response into message format (ASGI)\n",{"type":23,"tag":151,"props":4680,"children":4681},{"class":153,"line":272},[4682,4687,4692,4697],{"type":23,"tag":151,"props":4683,"children":4684},{"style":158},[4685],{"type":29,"value":4686},"for",{"type":23,"tag":151,"props":4688,"children":4689},{"style":164},[4690],{"type":29,"value":4691}," chunk ",{"type":23,"tag":151,"props":4693,"children":4694},{"style":158},[4695],{"type":29,"value":4696},"in",{"type":23,"tag":151,"props":4698,"children":4699},{"style":164},[4700],{"type":29,"value":4701}," AsgiHandler.encode_response(response):\n",{"type":23,"tag":151,"props":4703,"children":4704},{"class":153,"line":296},[4705],{"type":23,"tag":151,"props":4706,"children":4707},{"style":164},[4708],{"type":29,"value":4709},"message.reply_channel.send(chunk)\n",{"type":23,"tag":32,"props":4711,"children":4712},{},[4713,4715,4721,4723,4729],{"type":29,"value":4714},"Finally, we need to create an ",{"type":23,"tag":147,"props":4716,"children":4718},{"className":4717},[],[4719],{"type":29,"value":4720},"asgi.py",{"type":29,"value":4722}," file - this should sit right next to the ",{"type":23,"tag":147,"props":4724,"children":4726},{"className":4725},[],[4727],{"type":29,"value":4728},"wsgi.py",{"type":29,"value":4730}," file that was automatically created for you by Django.",{"type":23,"tag":32,"props":4732,"children":4733},{},[4734],{"type":29,"value":4735},"It's pretty simple:",{"type":23,"tag":141,"props":4737,"children":4739},{"className":143,"code":4738,"language":12,"meta":8,"style":8},"import os\nfrom channels.asgi import get_channel_layer\n\nos.environ.setdefault(\"DJANGO_SETTINGS_MODULE\", \"yourapp.settings\")\n\nchannel_layer = get_channel_layer()\n",[4740],{"type":23,"tag":147,"props":4741,"children":4742},{"__ignoreMap":8},[4743,4755,4776,4783,4809,4816],{"type":23,"tag":151,"props":4744,"children":4745},{"class":153,"line":154},[4746,4750],{"type":23,"tag":151,"props":4747,"children":4748},{"style":158},[4749],{"type":29,"value":172},{"type":23,"tag":151,"props":4751,"children":4752},{"style":164},[4753],{"type":29,"value":4754}," os\n",{"type":23,"tag":151,"props":4756,"children":4757},{"class":153,"line":180},[4758,4762,4767,4771],{"type":23,"tag":151,"props":4759,"children":4760},{"style":158},[4761],{"type":29,"value":161},{"type":23,"tag":151,"props":4763,"children":4764},{"style":164},[4765],{"type":29,"value":4766}," channels.asgi ",{"type":23,"tag":151,"props":4768,"children":4769},{"style":158},[4770],{"type":29,"value":172},{"type":23,"tag":151,"props":4772,"children":4773},{"style":164},[4774],{"type":29,"value":4775}," get_channel_layer\n",{"type":23,"tag":151,"props":4777,"children":4778},{"class":153,"line":190},[4779],{"type":23,"tag":151,"props":4780,"children":4781},{"emptyLinePlaceholder":184},[4782],{"type":29,"value":187},{"type":23,"tag":151,"props":4784,"children":4785},{"class":153,"line":220},[4786,4791,4796,4800,4805],{"type":23,"tag":151,"props":4787,"children":4788},{"style":164},[4789],{"type":29,"value":4790},"os.environ.setdefault(",{"type":23,"tag":151,"props":4792,"children":4793},{"style":285},[4794],{"type":29,"value":4795},"\"DJANGO_SETTINGS_MODULE\"",{"type":23,"tag":151,"props":4797,"children":4798},{"style":164},[4799],{"type":29,"value":370},{"type":23,"tag":151,"props":4801,"children":4802},{"style":285},[4803],{"type":29,"value":4804},"\"yourapp.settings\"",{"type":23,"tag":151,"props":4806,"children":4807},{"style":164},[4808],{"type":29,"value":293},{"type":23,"tag":151,"props":4810,"children":4811},{"class":153,"line":240},[4812],{"type":23,"tag":151,"props":4813,"children":4814},{"emptyLinePlaceholder":184},[4815],{"type":29,"value":187},{"type":23,"tag":151,"props":4817,"children":4818},{"class":153,"line":254},[4819,4824,4828],{"type":23,"tag":151,"props":4820,"children":4821},{"style":164},[4822],{"type":29,"value":4823},"channel_layer ",{"type":23,"tag":151,"props":4825,"children":4826},{"style":158},[4827],{"type":29,"value":544},{"type":23,"tag":151,"props":4829,"children":4830},{"style":164},[4831],{"type":29,"value":4832}," get_channel_layer()\n",{"type":23,"tag":32,"props":4834,"children":4835},{},[4836,4838,4844],{"type":29,"value":4837},"This is the file we're referencing in the supervisord config when we wrote ",{"type":23,"tag":147,"props":4839,"children":4841},{"className":4840},[],[4842],{"type":29,"value":4843},"command=/home/web/venv/bin/daphne -b 127.0.0.1 -p 8000 yourapp.asgi:channel_layer",{"type":29,"value":4845},", so now that it's in place, we have all the pieces in place.",{"type":23,"tag":32,"props":4847,"children":4848},{},[4849,4851,4857,4859,4864],{"type":29,"value":4850},"Restart the supervisord service (",{"type":23,"tag":147,"props":4852,"children":4854},{"className":4853},[],[4855],{"type":29,"value":4856},"service supervisord restart",{"type":29,"value":4858},"), and in a few seconds you should be able to check status (",{"type":23,"tag":147,"props":4860,"children":4862},{"className":4861},[],[4863],{"type":29,"value":3886},{"type":29,"value":4865},") and see both of our program entries up and running!",{"type":23,"tag":32,"props":4867,"children":4868},{},[4869],{"type":29,"value":4870},"Which is great! Give yourself a pat on the back. However, we have one step left. Right now, our interace server is bound to port 8000, which is fine for development, but we need to serve up requests on port 80 in production. Now, we could bind to port 80 instead, but that will require running Daphne with root permissions. Instead, let's borrow the common approach with WSGI servers, and setup a reverse proxy with Nginx.",{"type":23,"tag":117,"props":4872,"children":4874},{"id":4873},"last-step-nginx-setup",[4875],{"type":29,"value":4876},"Last Step - Nginx Setup",{"type":23,"tag":32,"props":4878,"children":4879},{},[4880,4882,4887],{"type":29,"value":4881},"Now, it doesn't ",{"type":23,"tag":428,"props":4883,"children":4884},{},[4885],{"type":29,"value":4886},"have",{"type":29,"value":4888}," to be Nginx - Apache could handle this duty, as could a number of others.",{"type":23,"tag":32,"props":4890,"children":4891},{},[4892],{"type":29,"value":4893},"In the meantime, however, Nginx it is, so let's take a look at the nicely straightforward setup.",{"type":23,"tag":32,"props":4895,"children":4896},{},[4897,4899,4905,4907,4913],{"type":29,"value":4898},"Head to ",{"type":23,"tag":147,"props":4900,"children":4902},{"className":4901},[],[4903],{"type":29,"value":4904},"/etc/nginx/sites-available/",{"type":29,"value":4906}," and create a new, appropriately named file (e.g. ",{"type":23,"tag":147,"props":4908,"children":4910},{"className":4909},[],[4911],{"type":29,"value":4912},"yourapp",{"type":29,"value":4914},"), and edit it to include the following:",{"type":23,"tag":141,"props":4916,"children":4918},{"className":143,"code":4917,"language":12,"meta":8,"style":8},"# Enable upgrading of connection (and websocket proxying) depending on the\n# presence of the upgrade field in the client request header\nmap \\$http_upgrade \\$connection_upgrade {\ndefault upgrade;\n'' close;\n}\n\n# Create an upstream alias to where we've set daphne to bind to\nupstream yourapp {\nserver 127.0.0.1:8000;\n}\n\nserver {\n\nlisten 80;\n# If you have a domain name, this is where to add it\nserver_name localhost;\n\nlocation / {\n# Pass request to the upstream alias\nproxy_pass http://yourapp;\n\n# Require http version 1.1 to allow for upgrade requests\nproxy_http_version 1.1;\n\n# We want proxy_buffering off for proxying to websockets.\nproxy_buffering off;\n\n# http://en.wikipedia.org/wiki/X-Forwarded-For\nproxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;\n\n# enable this if you use HTTPS:\n# proxy_set_header X-Forwarded-Proto https;\n\n# pass the Host: header from the client for the sake of redirects\nproxy_set_header Host $http_host;\n\n# We've set the Host header, so we don't need Nginx to muddle\n# about with redirects\nproxy_redirect off;\n\n# Depending on the request value, set the Upgrade and\n# connection headers\nproxy_set_header Upgrade $http_upgrade;\n\nproxy_set_header Connection $connection_upgrade;\n}\n}\n",[4919],{"type":23,"tag":147,"props":4920,"children":4921},{"__ignoreMap":8},[4922,4930,4938,4957,4970,4986,4993,5000,5008,5016,5043,5050,5057,5065,5072,5089,5097,5105,5112,5128,5136,5154,5161,5169,5186,5193,5201,5209,5216,5224,5261,5268,5276,5284,5291,5299,5316,5323,5331,5339,5347,5354,5362,5370,5387,5394,5411,5418],{"type":23,"tag":151,"props":4923,"children":4924},{"class":153,"line":154},[4925],{"type":23,"tag":151,"props":4926,"children":4927},{"style":324},[4928],{"type":29,"value":4929},"# Enable upgrading of connection (and websocket proxying) depending on the\n",{"type":23,"tag":151,"props":4931,"children":4932},{"class":153,"line":180},[4933],{"type":23,"tag":151,"props":4934,"children":4935},{"style":324},[4936],{"type":29,"value":4937},"# presence of the upgrade field in the client request header\n",{"type":23,"tag":151,"props":4939,"children":4940},{"class":153,"line":190},[4941,4946,4951],{"type":23,"tag":151,"props":4942,"children":4943},{"style":224},[4944],{"type":29,"value":4945},"map",{"type":23,"tag":151,"props":4947,"children":4948},{"style":164},[4949],{"type":29,"value":4950}," \\",{"type":23,"tag":151,"props":4952,"children":4954},{"style":4953},"--shiki-default:#B31D28;--shiki-default-font-style:italic;--shiki-dark:#FDAEB7;--shiki-dark-font-style:italic",[4955],{"type":29,"value":4956},"$http_upgrade \\$connection_upgrade {\n",{"type":23,"tag":151,"props":4958,"children":4959},{"class":153,"line":220},[4960,4965],{"type":23,"tag":151,"props":4961,"children":4962},{"style":164},[4963],{"type":29,"value":4964},"default upgrade",{"type":23,"tag":151,"props":4966,"children":4967},{"style":4953},[4968],{"type":29,"value":4969},";\n",{"type":23,"tag":151,"props":4971,"children":4972},{"class":153,"line":240},[4973,4977,4982],{"type":23,"tag":151,"props":4974,"children":4975},{"style":285},[4976],{"type":29,"value":4081},{"type":23,"tag":151,"props":4978,"children":4979},{"style":164},[4980],{"type":29,"value":4981}," close",{"type":23,"tag":151,"props":4983,"children":4984},{"style":4953},[4985],{"type":29,"value":4969},{"type":23,"tag":151,"props":4987,"children":4988},{"class":153,"line":254},[4989],{"type":23,"tag":151,"props":4990,"children":4991},{"style":164},[4992],{"type":29,"value":4098},{"type":23,"tag":151,"props":4994,"children":4995},{"class":153,"line":263},[4996],{"type":23,"tag":151,"props":4997,"children":4998},{"emptyLinePlaceholder":184},[4999],{"type":29,"value":187},{"type":23,"tag":151,"props":5001,"children":5002},{"class":153,"line":272},[5003],{"type":23,"tag":151,"props":5004,"children":5005},{"style":324},[5006],{"type":29,"value":5007},"# Create an upstream alias to where we've set daphne to bind to\n",{"type":23,"tag":151,"props":5009,"children":5010},{"class":153,"line":296},[5011],{"type":23,"tag":151,"props":5012,"children":5013},{"style":164},[5014],{"type":29,"value":5015},"upstream yourapp {\n",{"type":23,"tag":151,"props":5017,"children":5018},{"class":153,"line":320},[5019,5024,5029,5034,5039],{"type":23,"tag":151,"props":5020,"children":5021},{"style":164},[5022],{"type":29,"value":5023},"server ",{"type":23,"tag":151,"props":5025,"children":5026},{"style":224},[5027],{"type":29,"value":5028},"127.0",{"type":23,"tag":151,"props":5030,"children":5031},{"style":164},[5032],{"type":29,"value":5033},".0.1:",{"type":23,"tag":151,"props":5035,"children":5036},{"style":224},[5037],{"type":29,"value":5038},"8000",{"type":23,"tag":151,"props":5040,"children":5041},{"style":164},[5042],{"type":29,"value":4969},{"type":23,"tag":151,"props":5044,"children":5045},{"class":153,"line":330},[5046],{"type":23,"tag":151,"props":5047,"children":5048},{"style":164},[5049],{"type":29,"value":4098},{"type":23,"tag":151,"props":5051,"children":5052},{"class":153,"line":901},[5053],{"type":23,"tag":151,"props":5054,"children":5055},{"emptyLinePlaceholder":184},[5056],{"type":29,"value":187},{"type":23,"tag":151,"props":5058,"children":5059},{"class":153,"line":922},[5060],{"type":23,"tag":151,"props":5061,"children":5062},{"style":164},[5063],{"type":29,"value":5064},"server {\n",{"type":23,"tag":151,"props":5066,"children":5067},{"class":153,"line":930},[5068],{"type":23,"tag":151,"props":5069,"children":5070},{"emptyLinePlaceholder":184},[5071],{"type":29,"value":187},{"type":23,"tag":151,"props":5073,"children":5074},{"class":153,"line":971},[5075,5080,5085],{"type":23,"tag":151,"props":5076,"children":5077},{"style":164},[5078],{"type":29,"value":5079},"listen ",{"type":23,"tag":151,"props":5081,"children":5082},{"style":224},[5083],{"type":29,"value":5084},"80",{"type":23,"tag":151,"props":5086,"children":5087},{"style":164},[5088],{"type":29,"value":4969},{"type":23,"tag":151,"props":5090,"children":5091},{"class":153,"line":992},[5092],{"type":23,"tag":151,"props":5093,"children":5094},{"style":324},[5095],{"type":29,"value":5096},"# If you have a domain name, this is where to add it\n",{"type":23,"tag":151,"props":5098,"children":5099},{"class":153,"line":1000},[5100],{"type":23,"tag":151,"props":5101,"children":5102},{"style":164},[5103],{"type":29,"value":5104},"server_name localhost;\n",{"type":23,"tag":151,"props":5106,"children":5107},{"class":153,"line":1025},[5108],{"type":23,"tag":151,"props":5109,"children":5110},{"emptyLinePlaceholder":184},[5111],{"type":29,"value":187},{"type":23,"tag":151,"props":5113,"children":5114},{"class":153,"line":1046},[5115,5120,5124],{"type":23,"tag":151,"props":5116,"children":5117},{"style":164},[5118],{"type":29,"value":5119},"location ",{"type":23,"tag":151,"props":5121,"children":5122},{"style":158},[5123],{"type":29,"value":455},{"type":23,"tag":151,"props":5125,"children":5126},{"style":164},[5127],{"type":29,"value":3946},{"type":23,"tag":151,"props":5129,"children":5130},{"class":153,"line":1054},[5131],{"type":23,"tag":151,"props":5132,"children":5133},{"style":324},[5134],{"type":29,"value":5135},"# Pass request to the upstream alias\n",{"type":23,"tag":151,"props":5137,"children":5138},{"class":153,"line":1091},[5139,5144,5149],{"type":23,"tag":151,"props":5140,"children":5141},{"style":164},[5142],{"type":29,"value":5143},"proxy_pass http:",{"type":23,"tag":151,"props":5145,"children":5146},{"style":158},[5147],{"type":29,"value":5148},"//",{"type":23,"tag":151,"props":5150,"children":5151},{"style":164},[5152],{"type":29,"value":5153},"yourapp;\n",{"type":23,"tag":151,"props":5155,"children":5156},{"class":153,"line":1112},[5157],{"type":23,"tag":151,"props":5158,"children":5159},{"emptyLinePlaceholder":184},[5160],{"type":29,"value":187},{"type":23,"tag":151,"props":5162,"children":5163},{"class":153,"line":1120},[5164],{"type":23,"tag":151,"props":5165,"children":5166},{"style":324},[5167],{"type":29,"value":5168},"# Require http version 1.1 to allow for upgrade requests\n",{"type":23,"tag":151,"props":5170,"children":5171},{"class":153,"line":1129},[5172,5177,5182],{"type":23,"tag":151,"props":5173,"children":5174},{"style":164},[5175],{"type":29,"value":5176},"proxy_http_version ",{"type":23,"tag":151,"props":5178,"children":5179},{"style":224},[5180],{"type":29,"value":5181},"1.1",{"type":23,"tag":151,"props":5183,"children":5184},{"style":164},[5185],{"type":29,"value":4969},{"type":23,"tag":151,"props":5187,"children":5188},{"class":153,"line":1137},[5189],{"type":23,"tag":151,"props":5190,"children":5191},{"emptyLinePlaceholder":184},[5192],{"type":29,"value":187},{"type":23,"tag":151,"props":5194,"children":5195},{"class":153,"line":1146},[5196],{"type":23,"tag":151,"props":5197,"children":5198},{"style":324},[5199],{"type":29,"value":5200},"# We want proxy_buffering off for proxying to websockets.\n",{"type":23,"tag":151,"props":5202,"children":5203},{"class":153,"line":1155},[5204],{"type":23,"tag":151,"props":5205,"children":5206},{"style":164},[5207],{"type":29,"value":5208},"proxy_buffering off;\n",{"type":23,"tag":151,"props":5210,"children":5211},{"class":153,"line":2034},[5212],{"type":23,"tag":151,"props":5213,"children":5214},{"emptyLinePlaceholder":184},[5215],{"type":29,"value":187},{"type":23,"tag":151,"props":5217,"children":5218},{"class":153,"line":2043},[5219],{"type":23,"tag":151,"props":5220,"children":5221},{"style":324},[5222],{"type":29,"value":5223},"# http://en.wikipedia.org/wiki/X-Forwarded-For\n",{"type":23,"tag":151,"props":5225,"children":5226},{"class":153,"line":2052},[5227,5232,5237,5242,5246,5251,5256],{"type":23,"tag":151,"props":5228,"children":5229},{"style":164},[5230],{"type":29,"value":5231},"proxy_set_header X",{"type":23,"tag":151,"props":5233,"children":5234},{"style":158},[5235],{"type":29,"value":5236},"-",{"type":23,"tag":151,"props":5238,"children":5239},{"style":164},[5240],{"type":29,"value":5241},"Forwarded",{"type":23,"tag":151,"props":5243,"children":5244},{"style":158},[5245],{"type":29,"value":5236},{"type":23,"tag":151,"props":5247,"children":5248},{"style":164},[5249],{"type":29,"value":5250},"For ",{"type":23,"tag":151,"props":5252,"children":5253},{"style":4953},[5254],{"type":29,"value":5255},"$",{"type":23,"tag":151,"props":5257,"children":5258},{"style":164},[5259],{"type":29,"value":5260},"proxy_add_x_forwarded_for;\n",{"type":23,"tag":151,"props":5262,"children":5263},{"class":153,"line":2061},[5264],{"type":23,"tag":151,"props":5265,"children":5266},{"emptyLinePlaceholder":184},[5267],{"type":29,"value":187},{"type":23,"tag":151,"props":5269,"children":5270},{"class":153,"line":2070},[5271],{"type":23,"tag":151,"props":5272,"children":5273},{"style":324},[5274],{"type":29,"value":5275},"# enable this if you use HTTPS:\n",{"type":23,"tag":151,"props":5277,"children":5278},{"class":153,"line":2079},[5279],{"type":23,"tag":151,"props":5280,"children":5281},{"style":324},[5282],{"type":29,"value":5283},"# proxy_set_header X-Forwarded-Proto https;\n",{"type":23,"tag":151,"props":5285,"children":5286},{"class":153,"line":2088},[5287],{"type":23,"tag":151,"props":5288,"children":5289},{"emptyLinePlaceholder":184},[5290],{"type":29,"value":187},{"type":23,"tag":151,"props":5292,"children":5293},{"class":153,"line":2097},[5294],{"type":23,"tag":151,"props":5295,"children":5296},{"style":324},[5297],{"type":29,"value":5298},"# pass the Host: header from the client for the sake of redirects\n",{"type":23,"tag":151,"props":5300,"children":5301},{"class":153,"line":2106},[5302,5307,5311],{"type":23,"tag":151,"props":5303,"children":5304},{"style":164},[5305],{"type":29,"value":5306},"proxy_set_header Host ",{"type":23,"tag":151,"props":5308,"children":5309},{"style":4953},[5310],{"type":29,"value":5255},{"type":23,"tag":151,"props":5312,"children":5313},{"style":164},[5314],{"type":29,"value":5315},"http_host;\n",{"type":23,"tag":151,"props":5317,"children":5318},{"class":153,"line":2115},[5319],{"type":23,"tag":151,"props":5320,"children":5321},{"emptyLinePlaceholder":184},[5322],{"type":29,"value":187},{"type":23,"tag":151,"props":5324,"children":5325},{"class":153,"line":2124},[5326],{"type":23,"tag":151,"props":5327,"children":5328},{"style":324},[5329],{"type":29,"value":5330},"# We've set the Host header, so we don't need Nginx to muddle\n",{"type":23,"tag":151,"props":5332,"children":5333},{"class":153,"line":2133},[5334],{"type":23,"tag":151,"props":5335,"children":5336},{"style":324},[5337],{"type":29,"value":5338},"# about with redirects\n",{"type":23,"tag":151,"props":5340,"children":5341},{"class":153,"line":2142},[5342],{"type":23,"tag":151,"props":5343,"children":5344},{"style":164},[5345],{"type":29,"value":5346},"proxy_redirect off;\n",{"type":23,"tag":151,"props":5348,"children":5349},{"class":153,"line":2151},[5350],{"type":23,"tag":151,"props":5351,"children":5352},{"emptyLinePlaceholder":184},[5353],{"type":29,"value":187},{"type":23,"tag":151,"props":5355,"children":5356},{"class":153,"line":2160},[5357],{"type":23,"tag":151,"props":5358,"children":5359},{"style":324},[5360],{"type":29,"value":5361},"# Depending on the request value, set the Upgrade and\n",{"type":23,"tag":151,"props":5363,"children":5364},{"class":153,"line":2169},[5365],{"type":23,"tag":151,"props":5366,"children":5367},{"style":324},[5368],{"type":29,"value":5369},"# connection headers\n",{"type":23,"tag":151,"props":5371,"children":5372},{"class":153,"line":2178},[5373,5378,5382],{"type":23,"tag":151,"props":5374,"children":5375},{"style":164},[5376],{"type":29,"value":5377},"proxy_set_header Upgrade ",{"type":23,"tag":151,"props":5379,"children":5380},{"style":4953},[5381],{"type":29,"value":5255},{"type":23,"tag":151,"props":5383,"children":5384},{"style":164},[5385],{"type":29,"value":5386},"http_upgrade;\n",{"type":23,"tag":151,"props":5388,"children":5389},{"class":153,"line":2192},[5390],{"type":23,"tag":151,"props":5391,"children":5392},{"emptyLinePlaceholder":184},[5393],{"type":29,"value":187},{"type":23,"tag":151,"props":5395,"children":5396},{"class":153,"line":2200},[5397,5402,5406],{"type":23,"tag":151,"props":5398,"children":5399},{"style":164},[5400],{"type":29,"value":5401},"proxy_set_header Connection ",{"type":23,"tag":151,"props":5403,"children":5404},{"style":4953},[5405],{"type":29,"value":5255},{"type":23,"tag":151,"props":5407,"children":5408},{"style":164},[5409],{"type":29,"value":5410},"connection_upgrade;\n",{"type":23,"tag":151,"props":5412,"children":5413},{"class":153,"line":2229},[5414],{"type":23,"tag":151,"props":5415,"children":5416},{"style":164},[5417],{"type":29,"value":4098},{"type":23,"tag":151,"props":5419,"children":5421},{"class":153,"line":5420},48,[5422],{"type":23,"tag":151,"props":5423,"children":5424},{"style":164},[5425],{"type":29,"value":4098},{"type":23,"tag":5427,"props":5428,"children":5430},"h5",{"id":5429},"freebsd-note",[5431],{"type":29,"value":5432},"FreeBSD note:",{"type":23,"tag":32,"props":5434,"children":5435},{},[5436],{"type":29,"value":5437},"Chances are good that your Nginx conf may be in a directory like /usr/local/etc/nginx - you'll either need to alter it to support imports, or simply edit the file to include the above directives.",{"type":23,"tag":32,"props":5439,"children":5440},{},[5441,5443,5449,5451,5457],{"type":29,"value":5442},"Now, check your config for sanity with ",{"type":23,"tag":147,"props":5444,"children":5446},{"className":5445},[],[5447],{"type":29,"value":5448},"nginx -t",{"type":29,"value":5450}," and, if all is well, restart the nginx service ",{"type":23,"tag":147,"props":5452,"children":5454},{"className":5453},[],[5455],{"type":29,"value":5456},"service nginx restart",{"type":29,"value":386},{"type":23,"tag":32,"props":5459,"children":5460},{},[5461],{"type":29,"value":5462},"Now, push your changes to the server, and navigate to it in your browser. If is all well, you should see the simple message from the http.request consumer you set up. What is this feeling. Is this... joy?",{"type":23,"tag":24,"props":5464,"children":5466},{"id":5465},"next-time",[5467],{"type":29,"value":5468},"Next Time",{"type":23,"tag":32,"props":5470,"children":5471},{},[5472,5474,5479],{"type":29,"value":5473},"Well, now, all set up with a stack that wouldn't make a dev-op cry, not bad! It... doesn't really ",{"type":23,"tag":428,"props":5475,"children":5476},{},[5477],{"type":29,"value":5478},"do",{"type":29,"value":5480}," anything yet, though, does it? Next time, we'll be looking at getting some simple websocket communication up and running.",{"type":23,"tag":32,"props":5482,"children":5483},{},[5484,5486,5491],{"type":29,"value":5485},"Not a chat server, though. Everyone does a chat server. Instead, let's try something a little more ambitious - how about real time media from an ",{"type":23,"tag":428,"props":5487,"children":5488},{},[5489],{"type":29,"value":5490},"rtsp stream",{"type":29,"value":5492},", straight into the browser?",{"type":23,"tag":32,"props":5494,"children":5495},{},[5496,5498,5504],{"type":29,"value":5497},"If you're running into any issues with the above, I suggest that a quick question to the good folks at ",{"type":23,"tag":36,"props":5499,"children":5501},{"href":5500},"stackoverflow.com",[5502],{"type":29,"value":5503},"Stack Overflow",{"type":29,"value":5505}," will likely avail you best; and as always, if you notice any mistakes in the article, or just have something to say, the comment section awaits.",{"type":23,"tag":2836,"props":5507,"children":5508},{},[5509],{"type":29,"value":2840},{"title":8,"searchDepth":190,"depth":190,"links":5511},[5512,5513,5514,5515,5516,5517,5518],{"id":2922,"depth":190,"text":2925},{"id":3332,"depth":190,"text":3335},{"id":3432,"depth":190,"text":3435},{"id":3840,"depth":190,"text":3843},{"id":3872,"depth":190,"text":3875},{"id":4873,"depth":190,"text":4876},{"id":5465,"depth":180,"text":5468},"content:ckeefer:2016-4:djangochannels2.md","ckeefer/2016-4/djangochannels2.md","ckeefer/2016-4/djangochannels2",{"user":5523,"name":5524},"ckeefer","Christopher Keefer",{"_path":5526,"_dir":5527,"_draft":7,"_partial":7,"_locale":8,"title":5528,"description":5529,"publishDate":5530,"tags":5531,"excerpt":5529,"body":5532,"_type":2869,"_id":7187,"_source":2871,"_file":7188,"_stem":7189,"_extension":2874,"author":7190},"/ckeefer/2016-3/djangochannels1","2016-3","Django Channels: From the Ground Up - Part 1","You stare mournfully into the mass of code you've inherited. At some point, it's clear, the requirements called for the server to push information to the client, because there's an unholy mix of Server-Side Events, long-polling, hidden iframes and even a Java applet in there, all supporting some level of long-term connectivity with the server. It's almost fascinating in its barely functional hideousness, and you would be inclined to leave well enough alone... except for the new feature specifications you've been assigned, which require the client to be able to send data back to the server in response to the received events, in as close to real-time as you can get.","2016-06-13",[14,12,2885],{"type":20,"children":5533,"toc":7174},[5534,5555,5569,5575,5619,5624,5629,5643,5656,5669,5674,5681,5687,5701,5706,5712,5726,5731,5736,5774,5779,5797,5802,5807,5813,5818,5823,5914,5919,5958,5963,5987,6009,6014,6020,6025,6030,6056,6067,6072,6120,6126,6139,6144,6339,6344,6388,6392,6436,6442,6455,6483,6497,6502,6507,6518,6524,6538,6543,6582,6587,6628,6641,6645,6878,6882,7087,7107,7128,7140,7146,7158,7170],{"type":23,"tag":32,"props":5535,"children":5536},{},[5537,5539,5546,5548,5553],{"type":29,"value":5538},"You stare mournfully into the mass of code you've inherited. At some point, it's clear, the requirements called for the server to push information to the client, because there's an unholy mix of ",{"type":23,"tag":36,"props":5540,"children":5543},{"href":5541,"rel":5542},"https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events/Using_server-sent_events",[40],[5544],{"type":29,"value":5545},"Server-Side Events",{"type":29,"value":5547},", long-polling, hidden iframes and even a Java applet in there, all supporting some level of long-term connectivity with the server. It's almost fascinating in its barely functional hideousness, and you would be inclined to leave well enough alone... except for the ",{"type":23,"tag":428,"props":5549,"children":5550},{},[5551],{"type":29,"value":5552},"new",{"type":29,"value":5554}," feature specifications you've been assigned, which require the client to be able to send data back to the server in response to the received events, in as close to real-time as you can get.",{"type":23,"tag":32,"props":5556,"children":5557},{},[5558,5560,5567],{"type":29,"value":5559},"It's time for a major overhaul. Only ",{"type":23,"tag":36,"props":5561,"children":5564},{"href":5562,"rel":5563},"https://developer.mozilla.org/en-US/docs/Web/API/WebSockets_API",[40],[5565],{"type":29,"value":5566},"Websockets",{"type":29,"value":5568}," can save us now. And, as it so happens, one of your favourite frameworks just added support for real-time, bi-directional communication...",{"type":23,"tag":24,"props":5570,"children":5572},{"id":5571},"adding-more-batteries",[5573],{"type":29,"value":5574},"Adding More Batteries",{"type":23,"tag":32,"props":5576,"children":5577},{},[5578,5580,5585,5587,5592,5594,5600,5602,5609,5611,5618],{"type":29,"value":5579},"If you've been paying attention to the ",{"type":23,"tag":36,"props":5581,"children":5583},{"href":85,"rel":5582},[40],[5584],{"type":29,"value":89},{"type":29,"value":5586}," project, you've probably heard about ",{"type":23,"tag":36,"props":5588,"children":5590},{"href":2901,"rel":5589},[40],[5591],{"type":29,"value":2905},{"type":29,"value":5593},". A new server gateway interface (",{"type":23,"tag":36,"props":5595,"children":5598},{"href":5596,"rel":5597},"https://channels.readthedocs.io/en/latest/asgi.html",[40],[5599],{"type":29,"value":598},{"type":29,"value":5601},"), a new server (",{"type":23,"tag":36,"props":5603,"children":5606},{"href":5604,"rel":5605},"https://github.com/andrewgodwin/daphne",[40],[5607],{"type":29,"value":5608},"Daphne",{"type":29,"value":5610},") and, as a result, new functionality - including support for Websockets and (at some near-future point) ",{"type":23,"tag":36,"props":5612,"children":5615},{"href":5613,"rel":5614},"https://en.wikipedia.org/wiki/HTTP/2",[40],[5616],{"type":29,"value":5617},"HTTP2",{"type":29,"value":386},{"type":23,"tag":32,"props":5620,"children":5621},{},[5622],{"type":29,"value":5623},"Support for websockets isn't new - Node.js (and thus, Express, Meteor, Sails, etc.) had them pretty much out of the box, and its event-based model is well suited to them. Even on the Python side of things, if you needed websockets, you could have them - Gevent, Tornado/Cyclone and Twisted had your back, amongst others. In fact, the preferred server for Django Channels, Daphne, is based on Twisted.",{"type":23,"tag":32,"props":5625,"children":5626},{},[5627],{"type":29,"value":5628},"So, the big news here isn't OMG Websockets, but rather Websockets + Django. Pyramid and Flask fans may scoff, but Django's batteries-included approach echoes that of Python itself, and enables some solid work that tends to be easier to maintain than, for instance, the special snowflakes that a large Flask project can end up as (your mileage may vary).",{"type":23,"tag":32,"props":5630,"children":5631},{},[5632,5634,5641],{"type":29,"value":5633},"So, if you're sold on Django, there's good reason to be excited about Channels. Although its inclusion in Django core has been ",{"type":23,"tag":36,"props":5635,"children":5638},{"href":5636,"rel":5637},"https://groups.google.com/d/topic/django-developers/QRd4v7OErT8/discussion",[40],[5639],{"type":29,"value":5640},"deferred",{"type":29,"value":5642}," for now, chances are good that it will see integration with the Django core project at some near-future point, making now a good time to get up to speed.",{"type":23,"tag":32,"props":5644,"children":5645},{},[5646,5648,5654],{"type":29,"value":5647},"For this short series of articles, we'll be looking at doing just that. While the ",{"type":23,"tag":36,"props":5649,"children":5651},{"href":2901,"rel":5650},[40],[5652],{"type":29,"value":5653},"Django Channels documentation",{"type":29,"value":5655}," lives up to the high standard set by the core Django documentation, it doesn't spend much time describing what a production-ready setup might look like, or how to achieve it. Additionally, like most tutorials for websockets, it focuses on a simple echo or chat server. We're going to go a step beyond, and take a look at what we need to setup real-time streaming video in the browser.",{"type":23,"tag":32,"props":5657,"children":5658},{},[5659,5661,5667],{"type":29,"value":5660},"For the first two articles, though, we'll focus on how to get our shiny new Django Channels installation up and running. We're going to cover a lot of ground, so we won't be able to focus too closely on the details. If you run into trouble, a quick search might do it for you - if not, I suggest you bring your question to the good people at ",{"type":23,"tag":36,"props":5662,"children":5665},{"href":5663,"rel":5664},"https://stackoverflow.com",[40],[5666],{"type":29,"value":5503},{"type":29,"value":5668},"; and if you note any mistakes in the article, or just have something to say, the comment section below awaits you.",{"type":23,"tag":32,"props":5670,"children":5671},{},[5672],{"type":29,"value":5673},"Let's get started, shall we?",{"type":23,"tag":5675,"props":5676,"children":5678},"h4",{"id":5677},"penguins-or-daemons",[5679],{"type":29,"value":5680},"Penguins or Daemons?",{"type":23,"tag":5427,"props":5682,"children":5684},{"id":5683},"its-true-penguins-are-cuter-and-ive-never-had-to-kill-9-a-penguin",[5685],{"type":29,"value":5686},"It's true, penguins are cuter, and I've never had to kill -9 a penguin.",{"type":23,"tag":32,"props":5688,"children":5689},{},[5690,5692,5699],{"type":29,"value":5691},"One small additional wrinkle to the following documentation is that we're going to add some details for this same setup with ",{"type":23,"tag":36,"props":5693,"children":5696},{"href":5694,"rel":5695},"https://www.freebsd.org/",[40],[5697],{"type":29,"value":5698},"FreeBSD",{"type":29,"value":5700},". I've had some recent projects that have caused me to dip my toes into the BSD pool, and I noticed there's a relative lack of documentation for this sort of thing there, so we're going to help rectify that a little.",{"type":23,"tag":32,"props":5702,"children":5703},{},[5704],{"type":29,"value":5705},"Don't worry if you're planning to use a Linux flavour - in most cases, the instructions are either identical, or the differences will be called out.",{"type":23,"tag":24,"props":5707,"children":5709},{"id":5708},"from-the-ground-up",[5710],{"type":29,"value":5711},"From the Ground Up",{"type":23,"tag":32,"props":5713,"children":5714},{},[5715,5717,5724],{"type":29,"value":5716},"Assumption: You have a relatively modern unix-like operating system. Windows folks, as ever in server-land, I'm afraid you're on your own (maybe now's a good time to look into ",{"type":23,"tag":36,"props":5718,"children":5721},{"href":5719,"rel":5720},"https://msdn.microsoft.com/en-us/commandline/wsl/about",[40],[5722],{"type":29,"value":5723},"Ubuntu on Windows",{"type":29,"value":5725},"?).",{"type":23,"tag":32,"props":5727,"children":5728},{},[5729],{"type":29,"value":5730},"We're going to be re-treading some ground that is likely very familiar for some of you, so bear with me - we're going to start up from scratch here.",{"type":23,"tag":32,"props":5732,"children":5733},{},[5734],{"type":29,"value":5735},"Now, let's discuss our stack. We're going to go with a set of software that should look familiar to anyone who's set up a basic, fairly production-worthy server with Django:",{"type":23,"tag":2639,"props":5737,"children":5738},{},[5739,5744,5749,5754,5759,5764,5769],{"type":23,"tag":2643,"props":5740,"children":5741},{},[5742],{"type":29,"value":5743},"Python (2.7+ or 3.4+);",{"type":23,"tag":2643,"props":5745,"children":5746},{},[5747],{"type":29,"value":5748},"Virtualenv;",{"type":23,"tag":2643,"props":5750,"children":5751},{},[5752],{"type":29,"value":5753},"Pip;",{"type":23,"tag":2643,"props":5755,"children":5756},{},[5757],{"type":29,"value":5758},"Nginx;",{"type":23,"tag":2643,"props":5760,"children":5761},{},[5762],{"type":29,"value":5763},"Postgresql;",{"type":23,"tag":2643,"props":5765,"children":5766},{},[5767],{"type":29,"value":5768},"Supervisord;",{"type":23,"tag":2643,"props":5770,"children":5771},{},[5772],{"type":29,"value":5773},"Django (1.8+).",{"type":23,"tag":32,"props":5775,"children":5776},{},[5777],{"type":29,"value":5778},"And adding some new elements to support Channels:",{"type":23,"tag":2639,"props":5780,"children":5781},{},[5782,5787,5792],{"type":23,"tag":2643,"props":5783,"children":5784},{},[5785],{"type":29,"value":5786},"Redis;",{"type":23,"tag":2643,"props":5788,"children":5789},{},[5790],{"type":29,"value":5791},"Daphne;",{"type":23,"tag":2643,"props":5793,"children":5794},{},[5795],{"type":29,"value":5796},"channels.",{"type":23,"tag":32,"props":5798,"children":5799},{},[5800],{"type":29,"value":5801},"Let's go through the setup step-by-step, and discuss the how and, in some cases, the why of our software stack.",{"type":23,"tag":32,"props":5803,"children":5804},{},[5805],{"type":29,"value":5806},"For this first part, we're going to go get us far enough to run Django using our shiny new ASGI server, via the usual development invocation; and in part 2, we'll be getting our stack somewhere approaching production ready.",{"type":23,"tag":117,"props":5808,"children":5810},{"id":5809},"package-installation",[5811],{"type":29,"value":5812},"Package Installation",{"type":23,"tag":32,"props":5814,"children":5815},{},[5816],{"type":29,"value":5817},"This step is nice and straightforward. Ask your friendly neighbourhood package manager for more details.",{"type":23,"tag":32,"props":5819,"children":5820},{},[5821],{"type":29,"value":5822},"Debian/Ubuntu one-liner:",{"type":23,"tag":141,"props":5824,"children":5828},{"className":5825,"code":5826,"language":5827,"meta":8,"style":8},"language-shell shiki shiki-themes github-light github-dark","sudo apt-get -y -q install python python-dev python-pip python-virtualenv libpq-dev postgresql postgresql-contrib nginx supervisor python-software-properties redis-server\n","shell",[5829],{"type":23,"tag":147,"props":5830,"children":5831},{"__ignoreMap":8},[5832],{"type":23,"tag":151,"props":5833,"children":5834},{"class":153,"line":154},[5835,5840,5845,5850,5855,5859,5864,5869,5874,5879,5884,5889,5894,5899,5904,5909],{"type":23,"tag":151,"props":5836,"children":5837},{"style":199},[5838],{"type":29,"value":5839},"sudo",{"type":23,"tag":151,"props":5841,"children":5842},{"style":285},[5843],{"type":29,"value":5844}," apt-get",{"type":23,"tag":151,"props":5846,"children":5847},{"style":224},[5848],{"type":29,"value":5849}," -y",{"type":23,"tag":151,"props":5851,"children":5852},{"style":224},[5853],{"type":29,"value":5854}," -q",{"type":23,"tag":151,"props":5856,"children":5857},{"style":285},[5858],{"type":29,"value":3050},{"type":23,"tag":151,"props":5860,"children":5861},{"style":285},[5862],{"type":29,"value":5863}," python",{"type":23,"tag":151,"props":5865,"children":5866},{"style":285},[5867],{"type":29,"value":5868}," python-dev",{"type":23,"tag":151,"props":5870,"children":5871},{"style":285},[5872],{"type":29,"value":5873}," python-pip",{"type":23,"tag":151,"props":5875,"children":5876},{"style":285},[5877],{"type":29,"value":5878}," python-virtualenv",{"type":23,"tag":151,"props":5880,"children":5881},{"style":285},[5882],{"type":29,"value":5883}," libpq-dev",{"type":23,"tag":151,"props":5885,"children":5886},{"style":285},[5887],{"type":29,"value":5888}," postgresql",{"type":23,"tag":151,"props":5890,"children":5891},{"style":285},[5892],{"type":29,"value":5893}," postgresql-contrib",{"type":23,"tag":151,"props":5895,"children":5896},{"style":285},[5897],{"type":29,"value":5898}," nginx",{"type":23,"tag":151,"props":5900,"children":5901},{"style":285},[5902],{"type":29,"value":5903}," supervisor",{"type":23,"tag":151,"props":5905,"children":5906},{"style":285},[5907],{"type":29,"value":5908}," python-software-properties",{"type":23,"tag":151,"props":5910,"children":5911},{"style":285},[5912],{"type":29,"value":5913}," redis-server\n",{"type":23,"tag":32,"props":5915,"children":5916},{},[5917],{"type":29,"value":5918},"FreeBSD example:",{"type":23,"tag":141,"props":5920,"children":5922},{"className":5825,"code":5921,"language":5827,"meta":8,"style":8},"cd /usr/ports/databases/postgresql95-server/ && make install clean\n",[5923],{"type":23,"tag":147,"props":5924,"children":5925},{"__ignoreMap":8},[5926],{"type":23,"tag":151,"props":5927,"children":5928},{"class":153,"line":154},[5929,5934,5939,5944,5949,5953],{"type":23,"tag":151,"props":5930,"children":5931},{"style":224},[5932],{"type":29,"value":5933},"cd",{"type":23,"tag":151,"props":5935,"children":5936},{"style":285},[5937],{"type":29,"value":5938}," /usr/ports/databases/postgresql95-server/",{"type":23,"tag":151,"props":5940,"children":5941},{"style":164},[5942],{"type":29,"value":5943}," && ",{"type":23,"tag":151,"props":5945,"children":5946},{"style":199},[5947],{"type":29,"value":5948},"make",{"type":23,"tag":151,"props":5950,"children":5951},{"style":285},[5952],{"type":29,"value":3050},{"type":23,"tag":151,"props":5954,"children":5955},{"style":285},[5956],{"type":29,"value":5957}," clean\n",{"type":23,"tag":32,"props":5959,"children":5960},{},[5961],{"type":29,"value":5962},"OR",{"type":23,"tag":141,"props":5964,"children":5966},{"className":5825,"code":5965,"language":5827,"meta":8,"style":8},"pkg install postgresql95-server\n",[5967],{"type":23,"tag":147,"props":5968,"children":5969},{"__ignoreMap":8},[5970],{"type":23,"tag":151,"props":5971,"children":5972},{"class":153,"line":154},[5973,5978,5982],{"type":23,"tag":151,"props":5974,"children":5975},{"style":199},[5976],{"type":29,"value":5977},"pkg",{"type":23,"tag":151,"props":5979,"children":5980},{"style":285},[5981],{"type":29,"value":3050},{"type":23,"tag":151,"props":5983,"children":5984},{"style":285},[5985],{"type":29,"value":5986}," postgresql95-server\n",{"type":23,"tag":32,"props":5988,"children":5989},{},[5990,5992,5998,6000,6007],{"type":29,"value":5991},"and repeat for python27 (e.g. ",{"type":23,"tag":147,"props":5993,"children":5995},{"className":5994},[],[5996],{"type":29,"value":5997},"pkg install python27",{"type":29,"value":5999},"), nginx, supervisord, etc. I suggest searching ",{"type":23,"tag":36,"props":6001,"children":6004},{"href":6002,"rel":6003},"https://www.freshports.org/",[40],[6005],{"type":29,"value":6006},"FreshPorts",{"type":29,"value":6008}," for the specific package names.",{"type":23,"tag":32,"props":6010,"children":6011},{},[6012],{"type":29,"value":6013},"If you're on FreeBSD, you'll also want to add the appropriate enable commands to rc.conf to allow these applications to start with the server.",{"type":23,"tag":117,"props":6015,"children":6017},{"id":6016},"user-virtualenv-setup",[6018],{"type":29,"value":6019},"User & VirtualEnv setup",{"type":23,"tag":32,"props":6021,"children":6022},{},[6023],{"type":29,"value":6024},"Now that we have our system packages installed, let's set up our python virtual environment in preparation for getting our python packages into place.",{"type":23,"tag":32,"props":6026,"children":6027},{},[6028],{"type":29,"value":6029},"First, we'll create a new, low-priviledged user who we want to run our server software - running web-facing processes as root can be a security faux pas.",{"type":23,"tag":32,"props":6031,"children":6032},{},[6033,6039,6041,6046,6048,6054],{"type":23,"tag":147,"props":6034,"children":6036},{"className":6035},[],[6037],{"type":29,"value":6038},"sudo adduser web",{"type":29,"value":6040}," (If using FreeBSD, drop the ",{"type":23,"tag":147,"props":6042,"children":6044},{"className":6043},[],[6045],{"type":29,"value":5839},{"type":29,"value":6047}," and execute this (and other commands requiring sudo) as root by ",{"type":23,"tag":147,"props":6049,"children":6051},{"className":6050},[],[6052],{"type":29,"value":6053},"su",{"type":29,"value":6055},"ing into root account, or else install sudo package).",{"type":23,"tag":32,"props":6057,"children":6058},{},[6059,6061],{"type":29,"value":6060},"Let's su in as this user... ",{"type":23,"tag":147,"props":6062,"children":6064},{"className":6063},[],[6065],{"type":29,"value":6066},"su web",{"type":23,"tag":32,"props":6068,"children":6069},{},[6070],{"type":29,"value":6071},"We're now going to drop our virtualenv directory (and, in a moment, our deploy directory) into this users home dir - we don't have to do it this way, of course, but this avoids some bothersome chmod'ing and chown'ing we'd have to do otherwise.",{"type":23,"tag":141,"props":6073,"children":6075},{"className":2941,"code":6074,"language":2943,"meta":8,"style":8},"mkdir -p /home/web/venv/\n\nvirtualenv --no-site-packages /home/web/venv/\n",[6076],{"type":23,"tag":147,"props":6077,"children":6078},{"__ignoreMap":8},[6079,6096,6103],{"type":23,"tag":151,"props":6080,"children":6081},{"class":153,"line":154},[6082,6087,6091],{"type":23,"tag":151,"props":6083,"children":6084},{"style":199},[6085],{"type":29,"value":6086},"mkdir",{"type":23,"tag":151,"props":6088,"children":6089},{"style":224},[6090],{"type":29,"value":3647},{"type":23,"tag":151,"props":6092,"children":6093},{"style":285},[6094],{"type":29,"value":6095}," /home/web/venv/\n",{"type":23,"tag":151,"props":6097,"children":6098},{"class":153,"line":180},[6099],{"type":23,"tag":151,"props":6100,"children":6101},{"emptyLinePlaceholder":184},[6102],{"type":29,"value":187},{"type":23,"tag":151,"props":6104,"children":6105},{"class":153,"line":190},[6106,6111,6116],{"type":23,"tag":151,"props":6107,"children":6108},{"style":199},[6109],{"type":29,"value":6110},"virtualenv",{"type":23,"tag":151,"props":6112,"children":6113},{"style":224},[6114],{"type":29,"value":6115}," --no-site-packages",{"type":23,"tag":151,"props":6117,"children":6118},{"style":285},[6119],{"type":29,"value":6095},{"type":23,"tag":117,"props":6121,"children":6123},{"id":6122},"python-package-installation",[6124],{"type":29,"value":6125},"Python Package Installation",{"type":23,"tag":32,"props":6127,"children":6128},{},[6129,6131,6138],{"type":29,"value":6130},"Now that we have our virtualenv in place, we can install our python packages via pip. If we follow the git deploy setup, below, these can also be automatically installed/upgraded for us by processing a ",{"type":23,"tag":36,"props":6132,"children":6135},{"href":6133,"rel":6134},"https://pip.readthedocs.io/en/1.1/requirements.html",[40],[6136],{"type":29,"value":6137},"requirements.txt file",{"type":29,"value":386},{"type":23,"tag":32,"props":6140,"children":6141},{},[6142],{"type":29,"value":6143},"Let's make a requirements.txt file that looks like the following:",{"type":23,"tag":141,"props":6145,"children":6147},{"className":5825,"code":6146,"language":5827,"meta":8,"style":8},"asgi-redis==0.12.0\nasgiref==0.13.0\nautobahn==0.14.1\nchannels==0.14.0\ndaphne==0.12.1\nDjango>=1.9,=2.6,\u003C2.7\nredis==2.10.5\nsix==1.10.0\nTwisted==16.2.0\ntxaio==2.5.1\nzope.interface==4.1.3\n",[6148],{"type":23,"tag":147,"props":6149,"children":6150},{"__ignoreMap":8},[6151,6168,6185,6202,6219,6236,6258,6275,6292,6309,6326],{"type":23,"tag":151,"props":6152,"children":6153},{"class":153,"line":154},[6154,6159,6163],{"type":23,"tag":151,"props":6155,"children":6156},{"style":164},[6157],{"type":29,"value":6158},"asgi-redis",{"type":23,"tag":151,"props":6160,"children":6161},{"style":158},[6162],{"type":29,"value":544},{"type":23,"tag":151,"props":6164,"children":6165},{"style":285},[6166],{"type":29,"value":6167},"=0.12.0\n",{"type":23,"tag":151,"props":6169,"children":6170},{"class":153,"line":180},[6171,6176,6180],{"type":23,"tag":151,"props":6172,"children":6173},{"style":164},[6174],{"type":29,"value":6175},"asgiref",{"type":23,"tag":151,"props":6177,"children":6178},{"style":158},[6179],{"type":29,"value":544},{"type":23,"tag":151,"props":6181,"children":6182},{"style":285},[6183],{"type":29,"value":6184},"=0.13.0\n",{"type":23,"tag":151,"props":6186,"children":6187},{"class":153,"line":190},[6188,6193,6197],{"type":23,"tag":151,"props":6189,"children":6190},{"style":164},[6191],{"type":29,"value":6192},"autobahn",{"type":23,"tag":151,"props":6194,"children":6195},{"style":158},[6196],{"type":29,"value":544},{"type":23,"tag":151,"props":6198,"children":6199},{"style":285},[6200],{"type":29,"value":6201},"=0.14.1\n",{"type":23,"tag":151,"props":6203,"children":6204},{"class":153,"line":220},[6205,6210,6214],{"type":23,"tag":151,"props":6206,"children":6207},{"style":164},[6208],{"type":29,"value":6209},"channels",{"type":23,"tag":151,"props":6211,"children":6212},{"style":158},[6213],{"type":29,"value":544},{"type":23,"tag":151,"props":6215,"children":6216},{"style":285},[6217],{"type":29,"value":6218},"=0.14.0\n",{"type":23,"tag":151,"props":6220,"children":6221},{"class":153,"line":240},[6222,6227,6231],{"type":23,"tag":151,"props":6223,"children":6224},{"style":164},[6225],{"type":29,"value":6226},"daphne",{"type":23,"tag":151,"props":6228,"children":6229},{"style":158},[6230],{"type":29,"value":544},{"type":23,"tag":151,"props":6232,"children":6233},{"style":285},[6234],{"type":29,"value":6235},"=0.12.1\n",{"type":23,"tag":151,"props":6237,"children":6238},{"class":153,"line":254},[6239,6243,6248,6253],{"type":23,"tag":151,"props":6240,"children":6241},{"style":199},[6242],{"type":29,"value":89},{"type":23,"tag":151,"props":6244,"children":6245},{"style":164},[6246],{"type":29,"value":6247},">",{"type":23,"tag":151,"props":6249,"children":6250},{"style":285},[6251],{"type":29,"value":6252},"=1.9,=2.6,",{"type":23,"tag":151,"props":6254,"children":6255},{"style":164},[6256],{"type":29,"value":6257},"\u003C2.7\n",{"type":23,"tag":151,"props":6259,"children":6260},{"class":153,"line":263},[6261,6266,6270],{"type":23,"tag":151,"props":6262,"children":6263},{"style":164},[6264],{"type":29,"value":6265},"redis",{"type":23,"tag":151,"props":6267,"children":6268},{"style":158},[6269],{"type":29,"value":544},{"type":23,"tag":151,"props":6271,"children":6272},{"style":285},[6273],{"type":29,"value":6274},"=2.10.5\n",{"type":23,"tag":151,"props":6276,"children":6277},{"class":153,"line":272},[6278,6283,6287],{"type":23,"tag":151,"props":6279,"children":6280},{"style":164},[6281],{"type":29,"value":6282},"six",{"type":23,"tag":151,"props":6284,"children":6285},{"style":158},[6286],{"type":29,"value":544},{"type":23,"tag":151,"props":6288,"children":6289},{"style":285},[6290],{"type":29,"value":6291},"=1.10.0\n",{"type":23,"tag":151,"props":6293,"children":6294},{"class":153,"line":296},[6295,6300,6304],{"type":23,"tag":151,"props":6296,"children":6297},{"style":164},[6298],{"type":29,"value":6299},"Twisted",{"type":23,"tag":151,"props":6301,"children":6302},{"style":158},[6303],{"type":29,"value":544},{"type":23,"tag":151,"props":6305,"children":6306},{"style":285},[6307],{"type":29,"value":6308},"=16.2.0\n",{"type":23,"tag":151,"props":6310,"children":6311},{"class":153,"line":320},[6312,6317,6321],{"type":23,"tag":151,"props":6313,"children":6314},{"style":164},[6315],{"type":29,"value":6316},"txaio",{"type":23,"tag":151,"props":6318,"children":6319},{"style":158},[6320],{"type":29,"value":544},{"type":23,"tag":151,"props":6322,"children":6323},{"style":285},[6324],{"type":29,"value":6325},"=2.5.1\n",{"type":23,"tag":151,"props":6327,"children":6328},{"class":153,"line":330},[6329,6334],{"type":23,"tag":151,"props":6330,"children":6331},{"style":199},[6332],{"type":29,"value":6333},"zope.interface",{"type":23,"tag":151,"props":6335,"children":6336},{"style":285},[6337],{"type":29,"value":6338},"==4.1.3\n",{"type":23,"tag":32,"props":6340,"children":6341},{},[6342],{"type":29,"value":6343},"Now, we can activate the virtual environment and install these requirements with pip like so:",{"type":23,"tag":141,"props":6345,"children":6347},{"className":5825,"code":6346,"language":5827,"meta":8,"style":8},"source /home/web/venv/bin/activate\npip install -r requirements.txt\ndeactivate\n",[6348],{"type":23,"tag":147,"props":6349,"children":6350},{"__ignoreMap":8},[6351,6362,6381],{"type":23,"tag":151,"props":6352,"children":6353},{"class":153,"line":154},[6354,6358],{"type":23,"tag":151,"props":6355,"children":6356},{"style":224},[6357],{"type":29,"value":3009},{"type":23,"tag":151,"props":6359,"children":6360},{"style":285},[6361],{"type":29,"value":3014},{"type":23,"tag":151,"props":6363,"children":6364},{"class":153,"line":180},[6365,6369,6373,6377],{"type":23,"tag":151,"props":6366,"children":6367},{"style":199},[6368],{"type":29,"value":3045},{"type":23,"tag":151,"props":6370,"children":6371},{"style":285},[6372],{"type":29,"value":3050},{"type":23,"tag":151,"props":6374,"children":6375},{"style":224},[6376],{"type":29,"value":3060},{"type":23,"tag":151,"props":6378,"children":6379},{"style":285},[6380],{"type":29,"value":3065},{"type":23,"tag":151,"props":6382,"children":6383},{"class":153,"line":190},[6384],{"type":23,"tag":151,"props":6385,"children":6386},{"style":199},[6387],{"type":29,"value":3105},{"type":23,"tag":32,"props":6389,"children":6390},{},[6391],{"type":29,"value":3147},{"type":23,"tag":141,"props":6393,"children":6395},{"className":5825,"code":6394,"language":5827,"meta":8,"style":8},"source /home/web/venv/bin/activate.csh\npip install -r requirements.txt\ndeactivate\n",[6396],{"type":23,"tag":147,"props":6397,"children":6398},{"__ignoreMap":8},[6399,6410,6429],{"type":23,"tag":151,"props":6400,"children":6401},{"class":153,"line":154},[6402,6406],{"type":23,"tag":151,"props":6403,"children":6404},{"style":224},[6405],{"type":29,"value":3009},{"type":23,"tag":151,"props":6407,"children":6408},{"style":285},[6409],{"type":29,"value":3206},{"type":23,"tag":151,"props":6411,"children":6412},{"class":153,"line":180},[6413,6417,6421,6425],{"type":23,"tag":151,"props":6414,"children":6415},{"style":199},[6416],{"type":29,"value":3045},{"type":23,"tag":151,"props":6418,"children":6419},{"style":285},[6420],{"type":29,"value":3050},{"type":23,"tag":151,"props":6422,"children":6423},{"style":224},[6424],{"type":29,"value":3060},{"type":23,"tag":151,"props":6426,"children":6427},{"style":285},[6428],{"type":29,"value":3065},{"type":23,"tag":151,"props":6430,"children":6431},{"class":153,"line":190},[6432],{"type":23,"tag":151,"props":6433,"children":6434},{"style":199},[6435],{"type":29,"value":3105},{"type":23,"tag":117,"props":6437,"children":6439},{"id":6438},"django-project-setup",[6440],{"type":29,"value":6441},"Django Project Setup",{"type":23,"tag":32,"props":6443,"children":6444},{},[6445,6447,6454],{"type":29,"value":6446},"Now, we're going to get a bare minimal Django project structure in place. Thankfully, Django has some built-in ability to generate it's own boilerplate, which we'll take advantage of. For this, I refer you to the ",{"type":23,"tag":36,"props":6448,"children":6451},{"href":6449,"rel":6450},"https://docs.djangoproject.com/en/1.9/intro/tutorial01/#creating-a-project",[40],[6452],{"type":29,"value":6453},"official Django docs",{"type":29,"value":386},{"type":23,"tag":32,"props":6456,"children":6457},{},[6458,6460,6465,6467,6473,6475,6481],{"type":29,"value":6459},"For our purposes, the only file of interest is ",{"type":23,"tag":147,"props":6461,"children":6463},{"className":6462},[],[6464],{"type":29,"value":3913},{"type":29,"value":6466}," in your app directory. Open that up in your favourite editor, and find the ",{"type":23,"tag":147,"props":6468,"children":6470},{"className":6469},[],[6471],{"type":29,"value":6472},"INSTALLED_APPS",{"type":29,"value":6474}," tuple. Installing channels into Django is as simple as adding ",{"type":23,"tag":147,"props":6476,"children":6478},{"className":6477},[],[6479],{"type":29,"value":6480},"'channels'",{"type":29,"value":6482}," to the end of the tuple.",{"type":23,"tag":32,"props":6484,"children":6485},{},[6486,6488,6495],{"type":29,"value":6487},"You may also need/want to add your 'app_name' to this tuple, especially if you need static file discovery. You can take a look at the ",{"type":23,"tag":36,"props":6489,"children":6492},{"href":6490,"rel":6491},"https://channels.readthedocs.io/en/latest/installation.html",[40],[6493],{"type":29,"value":6494},"Channels Installation",{"type":29,"value":6496}," documentation for a little more detail here, but it's nicely straightforward.",{"type":23,"tag":32,"props":6498,"children":6499},{},[6500],{"type":29,"value":6501},"You should also copy that requirements.txt file we made earlier into the root of your app directory. If nothing else, it serves as a reference as to what packages you're using.",{"type":23,"tag":5427,"props":6503,"children":6504},{"id":5429},[6505],{"type":29,"value":6506},"FreeBSD Note:",{"type":23,"tag":32,"props":6508,"children":6509},{},[6510,6512],{"type":29,"value":6511},"At this point we'll be relying on SQLite to provide our database solution - in many distributions, this is built-in to Python, but on FreeBSD we'll also need to install the py-sqlite3 package: ",{"type":23,"tag":147,"props":6513,"children":6515},{"className":6514},[],[6516],{"type":29,"value":6517},"pkg install py27-sqlite3",{"type":23,"tag":117,"props":6519,"children":6521},{"id":6520},"deploy-setup-optional",[6522],{"type":29,"value":6523},"Deploy Setup [Optional]",{"type":23,"tag":32,"props":6525,"children":6526},{},[6527,6529,6536],{"type":29,"value":6528},"This step is purely optional, but I like it as an easy way to ",{"type":23,"tag":36,"props":6530,"children":6533},{"href":6531,"rel":6532},"https://artandlogic.com/2013/12/deploying-websites-with-git/",[40],[6534],{"type":29,"value":6535},"deploy via git push",{"type":29,"value":6537},". That link has more info, so we'll just sketch out the implementation here:",{"type":23,"tag":32,"props":6539,"children":6540},{},[6541],{"type":29,"value":6542},"Make the necessary directories:",{"type":23,"tag":141,"props":6544,"children":6546},{"className":5825,"code":6545,"language":5827,"meta":8,"style":8},"mkdir -p /home/web/www.git/\nmkdir -p /home/web/www/\n",[6547],{"type":23,"tag":147,"props":6548,"children":6549},{"__ignoreMap":8},[6550,6566],{"type":23,"tag":151,"props":6551,"children":6552},{"class":153,"line":154},[6553,6557,6561],{"type":23,"tag":151,"props":6554,"children":6555},{"style":199},[6556],{"type":29,"value":6086},{"type":23,"tag":151,"props":6558,"children":6559},{"style":224},[6560],{"type":29,"value":3647},{"type":23,"tag":151,"props":6562,"children":6563},{"style":285},[6564],{"type":29,"value":6565}," /home/web/www.git/\n",{"type":23,"tag":151,"props":6567,"children":6568},{"class":153,"line":180},[6569,6573,6577],{"type":23,"tag":151,"props":6570,"children":6571},{"style":199},[6572],{"type":29,"value":6086},{"type":23,"tag":151,"props":6574,"children":6575},{"style":224},[6576],{"type":29,"value":3647},{"type":23,"tag":151,"props":6578,"children":6579},{"style":285},[6580],{"type":29,"value":6581}," /home/web/www/\n",{"type":23,"tag":32,"props":6583,"children":6584},{},[6585],{"type":29,"value":6586},"Setup a bare git repository:",{"type":23,"tag":141,"props":6588,"children":6590},{"className":5825,"code":6589,"language":5827,"meta":8,"style":8},"cd /home/web/www.git/\ngit config core.sharedRepository group\n",[6591],{"type":23,"tag":147,"props":6592,"children":6593},{"__ignoreMap":8},[6594,6605],{"type":23,"tag":151,"props":6595,"children":6596},{"class":153,"line":154},[6597,6601],{"type":23,"tag":151,"props":6598,"children":6599},{"style":224},[6600],{"type":29,"value":5933},{"type":23,"tag":151,"props":6602,"children":6603},{"style":285},[6604],{"type":29,"value":6565},{"type":23,"tag":151,"props":6606,"children":6607},{"class":153,"line":180},[6608,6613,6618,6623],{"type":23,"tag":151,"props":6609,"children":6610},{"style":199},[6611],{"type":29,"value":6612},"git",{"type":23,"tag":151,"props":6614,"children":6615},{"style":285},[6616],{"type":29,"value":6617}," config",{"type":23,"tag":151,"props":6619,"children":6620},{"style":285},[6621],{"type":29,"value":6622}," core.sharedRepository",{"type":23,"tag":151,"props":6624,"children":6625},{"style":285},[6626],{"type":29,"value":6627}," group\n",{"type":23,"tag":32,"props":6629,"children":6630},{},[6631,6633,6639],{"type":29,"value":6632},"And create a post-receive hook that will ",{"type":23,"tag":147,"props":6634,"children":6636},{"className":6635},[],[6637],{"type":29,"value":6638},"install/update",{"type":29,"value":6640}," our packages, run migrations for us, and kill and restart the development server:",{"type":23,"tag":32,"props":6642,"children":6643},{},[6644],{"type":29,"value":2938},{"type":23,"tag":141,"props":6646,"children":6648},{"className":2941,"code":6647,"language":2943,"meta":8,"style":8},"#!/bin/bash\n\nGIT_WORK_TREE=/home/web/www/ git checkout -f\n\nsource /home/web/venv/bin/activate\npushd /home/web/www/\n\n# Install python libs via pip and perform database migrations\npip install --upgrade -r requirements.txt\npython manage.py migrate\n\nkill ps aux | grep runserver | grep -v grep | awk '{print $2}'\npython manage.py runserver\n\npopd\ndeactivate\n",[6649],{"type":23,"tag":147,"props":6650,"children":6651},{"__ignoreMap":8},[6652,6659,6666,6693,6700,6711,6718,6725,6732,6755,6770,6777,6841,6857,6864,6871],{"type":23,"tag":151,"props":6653,"children":6654},{"class":153,"line":154},[6655],{"type":23,"tag":151,"props":6656,"children":6657},{"style":324},[6658],{"type":29,"value":2955},{"type":23,"tag":151,"props":6660,"children":6661},{"class":153,"line":180},[6662],{"type":23,"tag":151,"props":6663,"children":6664},{"emptyLinePlaceholder":184},[6665],{"type":29,"value":187},{"type":23,"tag":151,"props":6667,"children":6668},{"class":153,"line":190},[6669,6673,6677,6681,6685,6689],{"type":23,"tag":151,"props":6670,"children":6671},{"style":164},[6672],{"type":29,"value":2970},{"type":23,"tag":151,"props":6674,"children":6675},{"style":158},[6676],{"type":29,"value":544},{"type":23,"tag":151,"props":6678,"children":6679},{"style":285},[6680],{"type":29,"value":2979},{"type":23,"tag":151,"props":6682,"children":6683},{"style":199},[6684],{"type":29,"value":2984},{"type":23,"tag":151,"props":6686,"children":6687},{"style":285},[6688],{"type":29,"value":2989},{"type":23,"tag":151,"props":6690,"children":6691},{"style":224},[6692],{"type":29,"value":2994},{"type":23,"tag":151,"props":6694,"children":6695},{"class":153,"line":220},[6696],{"type":23,"tag":151,"props":6697,"children":6698},{"emptyLinePlaceholder":184},[6699],{"type":29,"value":187},{"type":23,"tag":151,"props":6701,"children":6702},{"class":153,"line":240},[6703,6707],{"type":23,"tag":151,"props":6704,"children":6705},{"style":224},[6706],{"type":29,"value":3009},{"type":23,"tag":151,"props":6708,"children":6709},{"style":285},[6710],{"type":29,"value":3014},{"type":23,"tag":151,"props":6712,"children":6713},{"class":153,"line":254},[6714],{"type":23,"tag":151,"props":6715,"children":6716},{"style":164},[6717],{"type":29,"value":3022},{"type":23,"tag":151,"props":6719,"children":6720},{"class":153,"line":263},[6721],{"type":23,"tag":151,"props":6722,"children":6723},{"emptyLinePlaceholder":184},[6724],{"type":29,"value":187},{"type":23,"tag":151,"props":6726,"children":6727},{"class":153,"line":272},[6728],{"type":23,"tag":151,"props":6729,"children":6730},{"style":324},[6731],{"type":29,"value":3037},{"type":23,"tag":151,"props":6733,"children":6734},{"class":153,"line":296},[6735,6739,6743,6747,6751],{"type":23,"tag":151,"props":6736,"children":6737},{"style":199},[6738],{"type":29,"value":3045},{"type":23,"tag":151,"props":6740,"children":6741},{"style":285},[6742],{"type":29,"value":3050},{"type":23,"tag":151,"props":6744,"children":6745},{"style":224},[6746],{"type":29,"value":3055},{"type":23,"tag":151,"props":6748,"children":6749},{"style":224},[6750],{"type":29,"value":3060},{"type":23,"tag":151,"props":6752,"children":6753},{"style":285},[6754],{"type":29,"value":3065},{"type":23,"tag":151,"props":6756,"children":6757},{"class":153,"line":320},[6758,6762,6766],{"type":23,"tag":151,"props":6759,"children":6760},{"style":199},[6761],{"type":29,"value":12},{"type":23,"tag":151,"props":6763,"children":6764},{"style":285},[6765],{"type":29,"value":3077},{"type":23,"tag":151,"props":6767,"children":6768},{"style":285},[6769],{"type":29,"value":3082},{"type":23,"tag":151,"props":6771,"children":6772},{"class":153,"line":330},[6773],{"type":23,"tag":151,"props":6774,"children":6775},{"emptyLinePlaceholder":184},[6776],{"type":29,"value":187},{"type":23,"tag":151,"props":6778,"children":6779},{"class":153,"line":901},[6780,6785,6790,6795,6800,6805,6810,6814,6818,6823,6827,6831,6836],{"type":23,"tag":151,"props":6781,"children":6782},{"style":224},[6783],{"type":29,"value":6784},"kill",{"type":23,"tag":151,"props":6786,"children":6787},{"style":285},[6788],{"type":29,"value":6789}," ps",{"type":23,"tag":151,"props":6791,"children":6792},{"style":285},[6793],{"type":29,"value":6794}," aux",{"type":23,"tag":151,"props":6796,"children":6797},{"style":158},[6798],{"type":29,"value":6799}," |",{"type":23,"tag":151,"props":6801,"children":6802},{"style":199},[6803],{"type":29,"value":6804}," grep",{"type":23,"tag":151,"props":6806,"children":6807},{"style":285},[6808],{"type":29,"value":6809}," runserver",{"type":23,"tag":151,"props":6811,"children":6812},{"style":158},[6813],{"type":29,"value":6799},{"type":23,"tag":151,"props":6815,"children":6816},{"style":199},[6817],{"type":29,"value":6804},{"type":23,"tag":151,"props":6819,"children":6820},{"style":224},[6821],{"type":29,"value":6822}," -v",{"type":23,"tag":151,"props":6824,"children":6825},{"style":285},[6826],{"type":29,"value":6804},{"type":23,"tag":151,"props":6828,"children":6829},{"style":158},[6830],{"type":29,"value":6799},{"type":23,"tag":151,"props":6832,"children":6833},{"style":199},[6834],{"type":29,"value":6835}," awk",{"type":23,"tag":151,"props":6837,"children":6838},{"style":285},[6839],{"type":29,"value":6840}," '{print $2}'\n",{"type":23,"tag":151,"props":6842,"children":6843},{"class":153,"line":922},[6844,6848,6852],{"type":23,"tag":151,"props":6845,"children":6846},{"style":199},[6847],{"type":29,"value":12},{"type":23,"tag":151,"props":6849,"children":6850},{"style":285},[6851],{"type":29,"value":3077},{"type":23,"tag":151,"props":6853,"children":6854},{"style":285},[6855],{"type":29,"value":6856}," runserver\n",{"type":23,"tag":151,"props":6858,"children":6859},{"class":153,"line":930},[6860],{"type":23,"tag":151,"props":6861,"children":6862},{"emptyLinePlaceholder":184},[6863],{"type":29,"value":187},{"type":23,"tag":151,"props":6865,"children":6866},{"class":153,"line":971},[6867],{"type":23,"tag":151,"props":6868,"children":6869},{"style":164},[6870],{"type":29,"value":3097},{"type":23,"tag":151,"props":6872,"children":6873},{"class":153,"line":992},[6874],{"type":23,"tag":151,"props":6875,"children":6876},{"style":199},[6877],{"type":29,"value":3105},{"type":23,"tag":32,"props":6879,"children":6880},{},[6881],{"type":29,"value":3147},{"type":23,"tag":141,"props":6883,"children":6885},{"className":2941,"code":6884,"language":2943,"meta":8,"style":8},"#!/bin/csh\n\nenv GIT_WORK_TREE=/home/web/www/ git checkout -f\nsource /home/web/venv/bin/activate.csh\npushd /home/web/www/\n\npip install --upgrade -r requirements.txt\npython manage.py migrate\n\nkill ps aux | grep runserver | grep -v grep | awk '{print $2}'\npython manage.py runserver\n\npopd\ndeactivate\n",[6886],{"type":23,"tag":147,"props":6887,"children":6888},{"__ignoreMap":8},[6889,6896,6903,6926,6937,6944,6951,6974,6989,6996,7051,7066,7073,7080],{"type":23,"tag":151,"props":6890,"children":6891},{"class":153,"line":154},[6892],{"type":23,"tag":151,"props":6893,"children":6894},{"style":324},[6895],{"type":29,"value":3162},{"type":23,"tag":151,"props":6897,"children":6898},{"class":153,"line":180},[6899],{"type":23,"tag":151,"props":6900,"children":6901},{"emptyLinePlaceholder":184},[6902],{"type":29,"value":187},{"type":23,"tag":151,"props":6904,"children":6905},{"class":153,"line":190},[6906,6910,6914,6918,6922],{"type":23,"tag":151,"props":6907,"children":6908},{"style":199},[6909],{"type":29,"value":3177},{"type":23,"tag":151,"props":6911,"children":6912},{"style":285},[6913],{"type":29,"value":3182},{"type":23,"tag":151,"props":6915,"children":6916},{"style":285},[6917],{"type":29,"value":2984},{"type":23,"tag":151,"props":6919,"children":6920},{"style":285},[6921],{"type":29,"value":2989},{"type":23,"tag":151,"props":6923,"children":6924},{"style":224},[6925],{"type":29,"value":2994},{"type":23,"tag":151,"props":6927,"children":6928},{"class":153,"line":220},[6929,6933],{"type":23,"tag":151,"props":6930,"children":6931},{"style":224},[6932],{"type":29,"value":3009},{"type":23,"tag":151,"props":6934,"children":6935},{"style":285},[6936],{"type":29,"value":3206},{"type":23,"tag":151,"props":6938,"children":6939},{"class":153,"line":240},[6940],{"type":23,"tag":151,"props":6941,"children":6942},{"style":164},[6943],{"type":29,"value":3022},{"type":23,"tag":151,"props":6945,"children":6946},{"class":153,"line":254},[6947],{"type":23,"tag":151,"props":6948,"children":6949},{"emptyLinePlaceholder":184},[6950],{"type":29,"value":187},{"type":23,"tag":151,"props":6952,"children":6953},{"class":153,"line":263},[6954,6958,6962,6966,6970],{"type":23,"tag":151,"props":6955,"children":6956},{"style":199},[6957],{"type":29,"value":3045},{"type":23,"tag":151,"props":6959,"children":6960},{"style":285},[6961],{"type":29,"value":3050},{"type":23,"tag":151,"props":6963,"children":6964},{"style":224},[6965],{"type":29,"value":3055},{"type":23,"tag":151,"props":6967,"children":6968},{"style":224},[6969],{"type":29,"value":3060},{"type":23,"tag":151,"props":6971,"children":6972},{"style":285},[6973],{"type":29,"value":3065},{"type":23,"tag":151,"props":6975,"children":6976},{"class":153,"line":272},[6977,6981,6985],{"type":23,"tag":151,"props":6978,"children":6979},{"style":199},[6980],{"type":29,"value":12},{"type":23,"tag":151,"props":6982,"children":6983},{"style":285},[6984],{"type":29,"value":3077},{"type":23,"tag":151,"props":6986,"children":6987},{"style":285},[6988],{"type":29,"value":3082},{"type":23,"tag":151,"props":6990,"children":6991},{"class":153,"line":296},[6992],{"type":23,"tag":151,"props":6993,"children":6994},{"emptyLinePlaceholder":184},[6995],{"type":29,"value":187},{"type":23,"tag":151,"props":6997,"children":6998},{"class":153,"line":320},[6999,7003,7007,7011,7015,7019,7023,7027,7031,7035,7039,7043,7047],{"type":23,"tag":151,"props":7000,"children":7001},{"style":224},[7002],{"type":29,"value":6784},{"type":23,"tag":151,"props":7004,"children":7005},{"style":285},[7006],{"type":29,"value":6789},{"type":23,"tag":151,"props":7008,"children":7009},{"style":285},[7010],{"type":29,"value":6794},{"type":23,"tag":151,"props":7012,"children":7013},{"style":158},[7014],{"type":29,"value":6799},{"type":23,"tag":151,"props":7016,"children":7017},{"style":199},[7018],{"type":29,"value":6804},{"type":23,"tag":151,"props":7020,"children":7021},{"style":285},[7022],{"type":29,"value":6809},{"type":23,"tag":151,"props":7024,"children":7025},{"style":158},[7026],{"type":29,"value":6799},{"type":23,"tag":151,"props":7028,"children":7029},{"style":199},[7030],{"type":29,"value":6804},{"type":23,"tag":151,"props":7032,"children":7033},{"style":224},[7034],{"type":29,"value":6822},{"type":23,"tag":151,"props":7036,"children":7037},{"style":285},[7038],{"type":29,"value":6804},{"type":23,"tag":151,"props":7040,"children":7041},{"style":158},[7042],{"type":29,"value":6799},{"type":23,"tag":151,"props":7044,"children":7045},{"style":199},[7046],{"type":29,"value":6835},{"type":23,"tag":151,"props":7048,"children":7049},{"style":285},[7050],{"type":29,"value":6840},{"type":23,"tag":151,"props":7052,"children":7053},{"class":153,"line":330},[7054,7058,7062],{"type":23,"tag":151,"props":7055,"children":7056},{"style":199},[7057],{"type":29,"value":12},{"type":23,"tag":151,"props":7059,"children":7060},{"style":285},[7061],{"type":29,"value":3077},{"type":23,"tag":151,"props":7063,"children":7064},{"style":285},[7065],{"type":29,"value":6856},{"type":23,"tag":151,"props":7067,"children":7068},{"class":153,"line":901},[7069],{"type":23,"tag":151,"props":7070,"children":7071},{"emptyLinePlaceholder":184},[7072],{"type":29,"value":187},{"type":23,"tag":151,"props":7074,"children":7075},{"class":153,"line":922},[7076],{"type":23,"tag":151,"props":7077,"children":7078},{"style":164},[7079],{"type":29,"value":3097},{"type":23,"tag":151,"props":7081,"children":7082},{"class":153,"line":930},[7083],{"type":23,"tag":151,"props":7084,"children":7085},{"style":199},[7086],{"type":29,"value":3105},{"type":23,"tag":32,"props":7088,"children":7089},{},[7090,7092,7098,7100,7106],{"type":29,"value":7091},"Save this file as ",{"type":23,"tag":147,"props":7093,"children":7095},{"className":7094},[],[7096],{"type":29,"value":7097},"post-receive",{"type":29,"value":7099},", and then make it executable: ",{"type":23,"tag":147,"props":7101,"children":7103},{"className":7102},[],[7104],{"type":29,"value":7105},"chmod +x post-receive",{"type":29,"value":386},{"type":23,"tag":32,"props":7108,"children":7109},{},[7110,7112,7118,7120,7126],{"type":29,"value":7111},"Now, ",{"type":23,"tag":147,"props":7113,"children":7115},{"className":7114},[],[7116],{"type":29,"value":7117},"git init",{"type":29,"value":7119}," in your local directory, and add a new remote with ",{"type":23,"tag":147,"props":7121,"children":7123},{"className":7122},[],[7124],{"type":29,"value":7125},"git remote add your.server.addr.octets/home/web/www.git",{"type":29,"value":7127},". When pushing, do so as your web user, or you may run into permissions issues.",{"type":23,"tag":32,"props":7129,"children":7130},{},[7131,7133,7139],{"type":29,"value":7132},"As noted before, ",{"type":23,"tag":36,"props":7134,"children":7136},{"href":6531,"rel":7135},[40],[7137],{"type":29,"value":7138},"more details on this can be found in this article",{"type":29,"value":386},{"type":23,"tag":24,"props":7141,"children":7143},{"id":7142},"are-we-there-yet",[7144],{"type":29,"value":7145},"Are we there yet?",{"type":23,"tag":32,"props":7147,"children":7148},{},[7149,7151,7156],{"type":29,"value":7150},"Hah, no, but we are done for today. We're now at a point where you could push your code into your server, and use django's runserver command to run the development server. With the ",{"type":23,"tag":147,"props":7152,"children":7154},{"className":7153},[],[7155],{"type":29,"value":6209},{"type":29,"value":7157}," app installed, this well-known invocation will not start up the old wsgi server, but a shiny new ASGI server instead, with workers and interface living inside the same process as separate threads.",{"type":23,"tag":32,"props":7159,"children":7160},{},[7161,7168],{"type":23,"tag":36,"props":7162,"children":7165},{"href":7163,"rel":7164},"https://artandlogic.com/2016/06/django-channels-ground-part-2/",[40],[7166],{"type":29,"value":7167},"Next time",{"type":29,"value":7169},", we'll go the rest of the way - getting your server something resembling production ready, with postgresql setup, supervisord managing our server workers and interface, and we'll start figuring out what we need to actually use this setup for something cool.",{"type":23,"tag":2836,"props":7171,"children":7172},{},[7173],{"type":29,"value":2840},{"title":8,"searchDepth":190,"depth":190,"links":7175},[7176,7179,7186],{"id":5571,"depth":180,"text":5574,"children":7177},[7178],{"id":5677,"depth":220,"text":5680},{"id":5708,"depth":180,"text":5711,"children":7180},[7181,7182,7183,7184,7185],{"id":5809,"depth":190,"text":5812},{"id":6016,"depth":190,"text":6019},{"id":6122,"depth":190,"text":6125},{"id":6438,"depth":190,"text":6441},{"id":6520,"depth":190,"text":6523},{"id":7142,"depth":180,"text":7145},"content:ckeefer:2016-3:djangoChannels1.md","ckeefer/2016-3/djangoChannels1.md","ckeefer/2016-3/djangoChannels1",{"user":5523,"name":5524},{"_path":7192,"_dir":7193,"_draft":7,"_partial":7,"_locale":8,"title":7194,"description":7195,"publishDate":7196,"tags":7197,"excerpt":7195,"body":7200,"_type":2869,"_id":10567,"_source":2871,"_file":10568,"_stem":10569,"_extension":2874,"author":10570},"/ckeefer/2016-2/paymentprocessing","2016-2","Payment Processing with Braintree","You've built the web application of the century, and the users have rightly flooded to it. Cat pictures for everyone!","2016-05-11",[14,7198,7199,12],"js","jquery",{"type":20,"children":7201,"toc":10560},[7202,7206,7211,7223,7235,7249,7289,7294,7300,7319,7324,7330,7369,7374,7520,7534,7775,7794,7799,8025,8031,8063,8085,8431,8436,8449,8626,8631,8636,8644,8650,8672,9434,9440,9453,10502,10508,10513,10525,10537,10542,10556],{"type":23,"tag":32,"props":7203,"children":7204},{},[7205],{"type":29,"value":7195},{"type":23,"tag":32,"props":7207,"children":7208},{},[7209],{"type":29,"value":7210},"But alas, while your users indulge in cat-induced bliss, the cold hard reality of server costs cannot help but harsh your mellow. What is to be done?",{"type":23,"tag":32,"props":7212,"children":7213},{},[7214,7216,7221],{"type":29,"value":7215},"Maybe, you could get the users to... ",{"type":23,"tag":428,"props":7217,"children":7218},{},[7219],{"type":29,"value":7220},"pay",{"type":29,"value":7222}," for access to your incredible web application in all its multivarious splendour?",{"type":23,"tag":32,"props":7224,"children":7225},{},[7226,7233],{"type":23,"tag":36,"props":7227,"children":7230},{"href":7228,"rel":7229},"https://www.braintreepayments.com/",[40],[7231],{"type":29,"value":7232},"Braintree",{"type":29,"value":7234}," is a payment processor (now a subsidiary of PayPal), which boasts of a \"simple, robust way to accept payments\", and with features like a drop-in payment ui and libraries for various programming languages enabling fairly easy integration, is a solid choice for accepting payments via credit card or PayPal.",{"type":23,"tag":32,"props":7236,"children":7237},{},[7238,7240,7247],{"type":29,"value":7239},"While Braintree's ",{"type":23,"tag":36,"props":7241,"children":7244},{"href":7242,"rel":7243},"https://developers.braintreepayments.com/",[40],[7245],{"type":29,"value":7246},"developer documentation",{"type":29,"value":7248}," is blessedly detailed, it's possessed of a potentially confusing bevy of options, and its various implementation examples are spread out amongst a number of pages and platforms. So today, rather than reiterate any particular section of the docs, we're going to take a look at an end-to-end example of a specific, straightforward scenario - accepting and processing a one-time, immediately settled payment in a web application.",{"type":23,"tag":32,"props":7250,"children":7251},{},[7252,7254,7261,7263,7270,7272,7279,7280,7287],{"type":29,"value":7253},"Our languages of choice for today will be ",{"type":23,"tag":36,"props":7255,"children":7258},{"href":7256,"rel":7257},"https://developers.braintreepayments.com/start/hello-server/python",[40],[7259],{"type":29,"value":7260},"Python",{"type":29,"value":7262}," for the backend and, of course, ",{"type":23,"tag":36,"props":7264,"children":7267},{"href":7265,"rel":7266},"https://developers.braintreepayments.com/start/hello-client/javascript",[40],[7268],{"type":29,"value":7269},"JavaScript",{"type":29,"value":7271}," for the frontend, with the additional assumption of jQuery for the ease of $.ajax. The general logic for the backend will be portable to the other languages for which Braintree has released an SDK, e.g. ",{"type":23,"tag":36,"props":7273,"children":7276},{"href":7274,"rel":7275},"https://developers.braintreepayments.com/start/hello-server/ruby",[40],[7277],{"type":29,"value":7278},"Ruby",{"type":29,"value":370},{"type":23,"tag":36,"props":7281,"children":7284},{"href":7282,"rel":7283},"https://developers.braintreepayments.com/start/hello-server/php",[40],[7285],{"type":29,"value":7286},"PHP",{"type":29,"value":7288},", etc., and the frontend logic could certainly be implemented without jQuery.",{"type":23,"tag":32,"props":7290,"children":7291},{},[7292],{"type":29,"value":7293},"We're also going to assume the existence of a database for storing and retrieving item and transaction details. We're also going to assume the use of Django as our web framework in the following examples, but the general concepts are portable to other frameworks.",{"type":23,"tag":5427,"props":7295,"children":7297},{"id":7296},"not-production-ready",[7298],{"type":29,"value":7299},"Not Production Ready",{"type":23,"tag":32,"props":7301,"children":7302},{},[7303,7305,7310,7312,7317],{"type":29,"value":7304},"One more important note before we get started - the code below should ",{"type":23,"tag":2647,"props":7306,"children":7307},{},[7308],{"type":29,"value":7309},"not",{"type":29,"value":7311}," be considered production ready. It is example-quality, meant for instructional purposes only! While it should go without saying that copy-pasting code is always a bad idea, copy-pasting example code for business critical purposes like payment processing is a big enough no-no to warrant an extra paragraph warning everyone against it. ",{"type":23,"tag":428,"props":7313,"children":7314},{},[7315],{"type":29,"value":7316},"This is that paragraph.",{"type":29,"value":7318}," Copy-pasting == bad. Using this tutorial to learn == good.",{"type":23,"tag":32,"props":7320,"children":7321},{},[7322],{"type":29,"value":7323},"With that established, let's get started.",{"type":23,"tag":24,"props":7325,"children":7327},{"id":7326},"server-setup-and-token-generation",[7328],{"type":29,"value":7329},"Server Setup and Token Generation",{"type":23,"tag":32,"props":7331,"children":7332},{},[7333,7335,7342,7344,7351,7352,7358,7360,7367],{"type":29,"value":7334},"After ",{"type":23,"tag":36,"props":7336,"children":7339},{"href":7337,"rel":7338},"https://www.braintreepayments.com/get-started",[40],[7340],{"type":29,"value":7341},"signing up for a sandbox account",{"type":29,"value":7343}," and doing some basic setup in the ",{"type":23,"tag":36,"props":7345,"children":7348},{"href":7346,"rel":7347},"https://articles.braintreepayments.com/control-panel/basics/overview",[40],[7349],{"type":29,"value":7350},"braintree control panel",{"type":29,"value":370},{"type":23,"tag":36,"props":7353,"children":7355},{"href":7256,"rel":7354},[40],[7356],{"type":29,"value":7357},"our first step",{"type":29,"value":7359}," is to install the ",{"type":23,"tag":36,"props":7361,"children":7364},{"href":7362,"rel":7363},"https://pypi.python.org/pypi/braintree/3.25.0",[40],[7365],{"type":29,"value":7366},"Braintree Python Library",{"type":29,"value":7368},", manually or via pip.",{"type":23,"tag":32,"props":7370,"children":7371},{},[7372],{"type":29,"value":7373},"That done, we'll want to store our configuration details somewhere. In Django we might perform our configuration in settings.py:",{"type":23,"tag":141,"props":7375,"children":7377},{"className":143,"code":7376,"language":12,"meta":8,"style":8},"import os\nimport braintree\n\nbraintree.Configuration.configure(\n    os.environ.get('BT_ENVIRONMENT', braintree.Environment.Sandbox),\n    os.environ.get('BT_MERCHANT_ID', 'your_sandbox_merchant_id'),\n    os.environ.get('BT_PUBLIC_KEY', 'your_sandbox_public_key'),\n    os.environ.get('BT_PRIVATE_KEY', 'your_sandbox_private_key')\n)\n",[7378],{"type":23,"tag":147,"props":7379,"children":7380},{"__ignoreMap":8},[7381,7392,7404,7411,7419,7437,7463,7488,7513],{"type":23,"tag":151,"props":7382,"children":7383},{"class":153,"line":154},[7384,7388],{"type":23,"tag":151,"props":7385,"children":7386},{"style":158},[7387],{"type":29,"value":172},{"type":23,"tag":151,"props":7389,"children":7390},{"style":164},[7391],{"type":29,"value":4754},{"type":23,"tag":151,"props":7393,"children":7394},{"class":153,"line":180},[7395,7399],{"type":23,"tag":151,"props":7396,"children":7397},{"style":158},[7398],{"type":29,"value":172},{"type":23,"tag":151,"props":7400,"children":7401},{"style":164},[7402],{"type":29,"value":7403}," braintree\n",{"type":23,"tag":151,"props":7405,"children":7406},{"class":153,"line":190},[7407],{"type":23,"tag":151,"props":7408,"children":7409},{"emptyLinePlaceholder":184},[7410],{"type":29,"value":187},{"type":23,"tag":151,"props":7412,"children":7413},{"class":153,"line":220},[7414],{"type":23,"tag":151,"props":7415,"children":7416},{"style":164},[7417],{"type":29,"value":7418},"braintree.Configuration.configure(\n",{"type":23,"tag":151,"props":7420,"children":7421},{"class":153,"line":240},[7422,7427,7432],{"type":23,"tag":151,"props":7423,"children":7424},{"style":164},[7425],{"type":29,"value":7426},"    os.environ.get(",{"type":23,"tag":151,"props":7428,"children":7429},{"style":285},[7430],{"type":29,"value":7431},"'BT_ENVIRONMENT'",{"type":23,"tag":151,"props":7433,"children":7434},{"style":164},[7435],{"type":29,"value":7436},", braintree.Environment.Sandbox),\n",{"type":23,"tag":151,"props":7438,"children":7439},{"class":153,"line":254},[7440,7444,7449,7453,7458],{"type":23,"tag":151,"props":7441,"children":7442},{"style":164},[7443],{"type":29,"value":7426},{"type":23,"tag":151,"props":7445,"children":7446},{"style":285},[7447],{"type":29,"value":7448},"'BT_MERCHANT_ID'",{"type":23,"tag":151,"props":7450,"children":7451},{"style":164},[7452],{"type":29,"value":370},{"type":23,"tag":151,"props":7454,"children":7455},{"style":285},[7456],{"type":29,"value":7457},"'your_sandbox_merchant_id'",{"type":23,"tag":151,"props":7459,"children":7460},{"style":164},[7461],{"type":29,"value":7462},"),\n",{"type":23,"tag":151,"props":7464,"children":7465},{"class":153,"line":263},[7466,7470,7475,7479,7484],{"type":23,"tag":151,"props":7467,"children":7468},{"style":164},[7469],{"type":29,"value":7426},{"type":23,"tag":151,"props":7471,"children":7472},{"style":285},[7473],{"type":29,"value":7474},"'BT_PUBLIC_KEY'",{"type":23,"tag":151,"props":7476,"children":7477},{"style":164},[7478],{"type":29,"value":370},{"type":23,"tag":151,"props":7480,"children":7481},{"style":285},[7482],{"type":29,"value":7483},"'your_sandbox_public_key'",{"type":23,"tag":151,"props":7485,"children":7486},{"style":164},[7487],{"type":29,"value":7462},{"type":23,"tag":151,"props":7489,"children":7490},{"class":153,"line":272},[7491,7495,7500,7504,7509],{"type":23,"tag":151,"props":7492,"children":7493},{"style":164},[7494],{"type":29,"value":7426},{"type":23,"tag":151,"props":7496,"children":7497},{"style":285},[7498],{"type":29,"value":7499},"'BT_PRIVATE_KEY'",{"type":23,"tag":151,"props":7501,"children":7502},{"style":164},[7503],{"type":29,"value":370},{"type":23,"tag":151,"props":7505,"children":7506},{"style":285},[7507],{"type":29,"value":7508},"'your_sandbox_private_key'",{"type":23,"tag":151,"props":7510,"children":7511},{"style":164},[7512],{"type":29,"value":293},{"type":23,"tag":151,"props":7514,"children":7515},{"class":153,"line":296},[7516],{"type":23,"tag":151,"props":7517,"children":7518},{"style":164},[7519],{"type":29,"value":293},{"type":23,"tag":32,"props":7521,"children":7522},{},[7523,7525,7532],{"type":29,"value":7524},"Next, we need to setup an endpoint for the client to request a ",{"type":23,"tag":36,"props":7526,"children":7529},{"href":7527,"rel":7528},"https://developers.braintreepayments.com/start/hello-server/python#generate-a-client-token",[40],[7530],{"type":29,"value":7531},"client token",{"type":29,"value":7533}," from:",{"type":23,"tag":141,"props":7535,"children":7537},{"className":143,"code":7536,"language":12,"meta":8,"style":8},"import braintree\nfrom django.http import JsonResponse\nfrom django.views.decorators.http import require_http_methods\n\n@require_http_methods(['GET'])\ndef get_braintree_client_token(request):\n    \"\"\"\n    Generate and return client token.\n    \"\"\"\n    try:\n        client_token = braintree.ClientToken.generate()\n    except ValueError as e:\n        return JsonResponse({\"error\": e.message}, status=500)\n    return JsonResponse({\"token\": client_token})\n",[7538],{"type":23,"tag":147,"props":7539,"children":7540},{"__ignoreMap":8},[7541,7552,7572,7593,7600,7621,7638,7646,7654,7661,7673,7690,7713,7754],{"type":23,"tag":151,"props":7542,"children":7543},{"class":153,"line":154},[7544,7548],{"type":23,"tag":151,"props":7545,"children":7546},{"style":158},[7547],{"type":29,"value":172},{"type":23,"tag":151,"props":7549,"children":7550},{"style":164},[7551],{"type":29,"value":7403},{"type":23,"tag":151,"props":7553,"children":7554},{"class":153,"line":180},[7555,7559,7563,7567],{"type":23,"tag":151,"props":7556,"children":7557},{"style":158},[7558],{"type":29,"value":161},{"type":23,"tag":151,"props":7560,"children":7561},{"style":164},[7562],{"type":29,"value":4558},{"type":23,"tag":151,"props":7564,"children":7565},{"style":158},[7566],{"type":29,"value":172},{"type":23,"tag":151,"props":7568,"children":7569},{"style":164},[7570],{"type":29,"value":7571}," JsonResponse\n",{"type":23,"tag":151,"props":7573,"children":7574},{"class":153,"line":190},[7575,7579,7584,7588],{"type":23,"tag":151,"props":7576,"children":7577},{"style":158},[7578],{"type":29,"value":161},{"type":23,"tag":151,"props":7580,"children":7581},{"style":164},[7582],{"type":29,"value":7583}," django.views.decorators.http ",{"type":23,"tag":151,"props":7585,"children":7586},{"style":158},[7587],{"type":29,"value":172},{"type":23,"tag":151,"props":7589,"children":7590},{"style":164},[7591],{"type":29,"value":7592}," require_http_methods\n",{"type":23,"tag":151,"props":7594,"children":7595},{"class":153,"line":220},[7596],{"type":23,"tag":151,"props":7597,"children":7598},{"emptyLinePlaceholder":184},[7599],{"type":29,"value":187},{"type":23,"tag":151,"props":7601,"children":7602},{"class":153,"line":240},[7603,7608,7613,7617],{"type":23,"tag":151,"props":7604,"children":7605},{"style":199},[7606],{"type":29,"value":7607},"@require_http_methods",{"type":23,"tag":151,"props":7609,"children":7610},{"style":164},[7611],{"type":29,"value":7612},"([",{"type":23,"tag":151,"props":7614,"children":7615},{"style":285},[7616],{"type":29,"value":1958},{"type":23,"tag":151,"props":7618,"children":7619},{"style":164},[7620],{"type":29,"value":1963},{"type":23,"tag":151,"props":7622,"children":7623},{"class":153,"line":254},[7624,7628,7633],{"type":23,"tag":151,"props":7625,"children":7626},{"style":158},[7627],{"type":29,"value":794},{"type":23,"tag":151,"props":7629,"children":7630},{"style":199},[7631],{"type":29,"value":7632}," get_braintree_client_token",{"type":23,"tag":151,"props":7634,"children":7635},{"style":164},[7636],{"type":29,"value":7637},"(request):\n",{"type":23,"tag":151,"props":7639,"children":7640},{"class":153,"line":263},[7641],{"type":23,"tag":151,"props":7642,"children":7643},{"style":285},[7644],{"type":29,"value":7645},"    \"\"\"\n",{"type":23,"tag":151,"props":7647,"children":7648},{"class":153,"line":272},[7649],{"type":23,"tag":151,"props":7650,"children":7651},{"style":285},[7652],{"type":29,"value":7653},"    Generate and return client token.\n",{"type":23,"tag":151,"props":7655,"children":7656},{"class":153,"line":296},[7657],{"type":23,"tag":151,"props":7658,"children":7659},{"style":285},[7660],{"type":29,"value":7645},{"type":23,"tag":151,"props":7662,"children":7663},{"class":153,"line":320},[7664,7669],{"type":23,"tag":151,"props":7665,"children":7666},{"style":158},[7667],{"type":29,"value":7668},"    try",{"type":23,"tag":151,"props":7670,"children":7671},{"style":164},[7672],{"type":29,"value":2226},{"type":23,"tag":151,"props":7674,"children":7675},{"class":153,"line":330},[7676,7681,7685],{"type":23,"tag":151,"props":7677,"children":7678},{"style":164},[7679],{"type":29,"value":7680},"        client_token ",{"type":23,"tag":151,"props":7682,"children":7683},{"style":158},[7684],{"type":29,"value":544},{"type":23,"tag":151,"props":7686,"children":7687},{"style":164},[7688],{"type":29,"value":7689}," braintree.ClientToken.generate()\n",{"type":23,"tag":151,"props":7691,"children":7692},{"class":153,"line":901},[7693,7698,7703,7708],{"type":23,"tag":151,"props":7694,"children":7695},{"style":158},[7696],{"type":29,"value":7697},"    except",{"type":23,"tag":151,"props":7699,"children":7700},{"style":224},[7701],{"type":29,"value":7702}," ValueError",{"type":23,"tag":151,"props":7704,"children":7705},{"style":158},[7706],{"type":29,"value":7707}," as",{"type":23,"tag":151,"props":7709,"children":7710},{"style":164},[7711],{"type":29,"value":7712}," e:\n",{"type":23,"tag":151,"props":7714,"children":7715},{"class":153,"line":922},[7716,7721,7726,7731,7736,7741,7745,7750],{"type":23,"tag":151,"props":7717,"children":7718},{"style":158},[7719],{"type":29,"value":7720},"        return",{"type":23,"tag":151,"props":7722,"children":7723},{"style":164},[7724],{"type":29,"value":7725}," JsonResponse({",{"type":23,"tag":151,"props":7727,"children":7728},{"style":285},[7729],{"type":29,"value":7730},"\"error\"",{"type":23,"tag":151,"props":7732,"children":7733},{"style":164},[7734],{"type":29,"value":7735},": e.message}, ",{"type":23,"tag":151,"props":7737,"children":7738},{"style":764},[7739],{"type":29,"value":7740},"status",{"type":23,"tag":151,"props":7742,"children":7743},{"style":158},[7744],{"type":29,"value":544},{"type":23,"tag":151,"props":7746,"children":7747},{"style":224},[7748],{"type":29,"value":7749},"500",{"type":23,"tag":151,"props":7751,"children":7752},{"style":164},[7753],{"type":29,"value":293},{"type":23,"tag":151,"props":7755,"children":7756},{"class":153,"line":930},[7757,7761,7765,7770],{"type":23,"tag":151,"props":7758,"children":7759},{"style":158},[7760],{"type":29,"value":336},{"type":23,"tag":151,"props":7762,"children":7763},{"style":164},[7764],{"type":29,"value":7725},{"type":23,"tag":151,"props":7766,"children":7767},{"style":285},[7768],{"type":29,"value":7769},"\"token\"",{"type":23,"tag":151,"props":7771,"children":7772},{"style":164},[7773],{"type":29,"value":7774},": client_token})\n",{"type":23,"tag":32,"props":7776,"children":7777},{},[7778,7780,7785,7787,7793],{"type":29,"value":7779},"For Django, we can add this to a reasonable route in our ",{"type":23,"tag":147,"props":7781,"children":7783},{"className":7782},[],[7784],{"type":29,"value":4277},{"type":29,"value":7786}," file, and then make ajax requests against this route to generate and return the client token that the javascript braintree library requires. For the purpose of this tutorial, we'll assume we've bound it to ",{"type":23,"tag":147,"props":7788,"children":7790},{"className":7789},[],[7791],{"type":29,"value":7792},"/payment/token/",{"type":29,"value":386},{"type":23,"tag":32,"props":7795,"children":7796},{},[7797],{"type":29,"value":7798},"As an aside, we could also generate this token as part of the view and pass it into the context if we knew we were going to need it immediately, or if there were some other reason why an ajax roundtrip is undesirable. This could be as simple as:",{"type":23,"tag":141,"props":7800,"children":7802},{"className":143,"code":7801,"language":12,"meta":8,"style":8},"import braintree\nfrom django.shortcuts import render\nfrom django.http import HttpResponse\n\ndef start_payment_view(request, template_name=\"start_payment.html\"):\n    \"\"\"\n    Generate client token and pass it in the view context.\n    \"\"\"\n    try:\n        client_token = braintree.ClientToken.generate()\n    except ValueError as e:\n        return HttpResponse(\"Failed to generate Braintree client token\", status=500)\n\n    return render(request, template_name, {\"bt_client_token\": client_token})\n",[7803],{"type":23,"tag":147,"props":7804,"children":7805},{"__ignoreMap":8},[7806,7817,7838,7857,7864,7894,7901,7909,7916,7927,7942,7961,7997,8004],{"type":23,"tag":151,"props":7807,"children":7808},{"class":153,"line":154},[7809,7813],{"type":23,"tag":151,"props":7810,"children":7811},{"style":158},[7812],{"type":29,"value":172},{"type":23,"tag":151,"props":7814,"children":7815},{"style":164},[7816],{"type":29,"value":7403},{"type":23,"tag":151,"props":7818,"children":7819},{"class":153,"line":180},[7820,7824,7829,7833],{"type":23,"tag":151,"props":7821,"children":7822},{"style":158},[7823],{"type":29,"value":161},{"type":23,"tag":151,"props":7825,"children":7826},{"style":164},[7827],{"type":29,"value":7828}," django.shortcuts ",{"type":23,"tag":151,"props":7830,"children":7831},{"style":158},[7832],{"type":29,"value":172},{"type":23,"tag":151,"props":7834,"children":7835},{"style":164},[7836],{"type":29,"value":7837}," render\n",{"type":23,"tag":151,"props":7839,"children":7840},{"class":153,"line":190},[7841,7845,7849,7853],{"type":23,"tag":151,"props":7842,"children":7843},{"style":158},[7844],{"type":29,"value":161},{"type":23,"tag":151,"props":7846,"children":7847},{"style":164},[7848],{"type":29,"value":4558},{"type":23,"tag":151,"props":7850,"children":7851},{"style":158},[7852],{"type":29,"value":172},{"type":23,"tag":151,"props":7854,"children":7855},{"style":164},[7856],{"type":29,"value":4567},{"type":23,"tag":151,"props":7858,"children":7859},{"class":153,"line":220},[7860],{"type":23,"tag":151,"props":7861,"children":7862},{"emptyLinePlaceholder":184},[7863],{"type":29,"value":187},{"type":23,"tag":151,"props":7865,"children":7866},{"class":153,"line":240},[7867,7871,7876,7881,7885,7890],{"type":23,"tag":151,"props":7868,"children":7869},{"style":158},[7870],{"type":29,"value":794},{"type":23,"tag":151,"props":7872,"children":7873},{"style":199},[7874],{"type":29,"value":7875}," start_payment_view",{"type":23,"tag":151,"props":7877,"children":7878},{"style":164},[7879],{"type":29,"value":7880},"(request, template_name",{"type":23,"tag":151,"props":7882,"children":7883},{"style":158},[7884],{"type":29,"value":544},{"type":23,"tag":151,"props":7886,"children":7887},{"style":285},[7888],{"type":29,"value":7889},"\"start_payment.html\"",{"type":23,"tag":151,"props":7891,"children":7892},{"style":164},[7893],{"type":29,"value":217},{"type":23,"tag":151,"props":7895,"children":7896},{"class":153,"line":254},[7897],{"type":23,"tag":151,"props":7898,"children":7899},{"style":285},[7900],{"type":29,"value":7645},{"type":23,"tag":151,"props":7902,"children":7903},{"class":153,"line":263},[7904],{"type":23,"tag":151,"props":7905,"children":7906},{"style":285},[7907],{"type":29,"value":7908},"    Generate client token and pass it in the view context.\n",{"type":23,"tag":151,"props":7910,"children":7911},{"class":153,"line":272},[7912],{"type":23,"tag":151,"props":7913,"children":7914},{"style":285},[7915],{"type":29,"value":7645},{"type":23,"tag":151,"props":7917,"children":7918},{"class":153,"line":296},[7919,7923],{"type":23,"tag":151,"props":7920,"children":7921},{"style":158},[7922],{"type":29,"value":7668},{"type":23,"tag":151,"props":7924,"children":7925},{"style":164},[7926],{"type":29,"value":2226},{"type":23,"tag":151,"props":7928,"children":7929},{"class":153,"line":320},[7930,7934,7938],{"type":23,"tag":151,"props":7931,"children":7932},{"style":164},[7933],{"type":29,"value":7680},{"type":23,"tag":151,"props":7935,"children":7936},{"style":158},[7937],{"type":29,"value":544},{"type":23,"tag":151,"props":7939,"children":7940},{"style":164},[7941],{"type":29,"value":7689},{"type":23,"tag":151,"props":7943,"children":7944},{"class":153,"line":330},[7945,7949,7953,7957],{"type":23,"tag":151,"props":7946,"children":7947},{"style":158},[7948],{"type":29,"value":7697},{"type":23,"tag":151,"props":7950,"children":7951},{"style":224},[7952],{"type":29,"value":7702},{"type":23,"tag":151,"props":7954,"children":7955},{"style":158},[7956],{"type":29,"value":7707},{"type":23,"tag":151,"props":7958,"children":7959},{"style":164},[7960],{"type":29,"value":7712},{"type":23,"tag":151,"props":7962,"children":7963},{"class":153,"line":901},[7964,7968,7972,7977,7981,7985,7989,7993],{"type":23,"tag":151,"props":7965,"children":7966},{"style":158},[7967],{"type":29,"value":7720},{"type":23,"tag":151,"props":7969,"children":7970},{"style":164},[7971],{"type":29,"value":4637},{"type":23,"tag":151,"props":7973,"children":7974},{"style":285},[7975],{"type":29,"value":7976},"\"Failed to generate Braintree client token\"",{"type":23,"tag":151,"props":7978,"children":7979},{"style":164},[7980],{"type":29,"value":370},{"type":23,"tag":151,"props":7982,"children":7983},{"style":764},[7984],{"type":29,"value":7740},{"type":23,"tag":151,"props":7986,"children":7987},{"style":158},[7988],{"type":29,"value":544},{"type":23,"tag":151,"props":7990,"children":7991},{"style":224},[7992],{"type":29,"value":7749},{"type":23,"tag":151,"props":7994,"children":7995},{"style":164},[7996],{"type":29,"value":293},{"type":23,"tag":151,"props":7998,"children":7999},{"class":153,"line":922},[8000],{"type":23,"tag":151,"props":8001,"children":8002},{"emptyLinePlaceholder":184},[8003],{"type":29,"value":187},{"type":23,"tag":151,"props":8005,"children":8006},{"class":153,"line":930},[8007,8011,8016,8021],{"type":23,"tag":151,"props":8008,"children":8009},{"style":158},[8010],{"type":29,"value":336},{"type":23,"tag":151,"props":8012,"children":8013},{"style":164},[8014],{"type":29,"value":8015}," render(request, template_name, {",{"type":23,"tag":151,"props":8017,"children":8018},{"style":285},[8019],{"type":29,"value":8020},"\"bt_client_token\"",{"type":23,"tag":151,"props":8022,"children":8023},{"style":164},[8024],{"type":29,"value":7774},{"type":23,"tag":24,"props":8026,"children":8028},{"id":8027},"client-setup",[8029],{"type":29,"value":8030},"Client Setup",{"type":23,"tag":32,"props":8032,"children":8033},{},[8034,8036,8043,8045,8052,8054,8061],{"type":29,"value":8035},"Now for ",{"type":23,"tag":36,"props":8037,"children":8040},{"href":8038,"rel":8039},"https://developers.braintreepayments.com/reference/client-reference/javascript/v2/configuration",[40],[8041],{"type":29,"value":8042},"client side configuration",{"type":29,"value":8044},". On the client side, we're going to assume we want to use the ",{"type":23,"tag":36,"props":8046,"children":8049},{"href":8047,"rel":8048},"https://developers.braintreepayments.com/guides/drop-in/javascript/v2",[40],[8050],{"type":29,"value":8051},"'Drop-in'",{"type":29,"value":8053}," integration. This generates an iframe hosted by Braintree, allowing our application to qualify for the lowest-effort level of ",{"type":23,"tag":36,"props":8055,"children":8058},{"href":8056,"rel":8057},"https://en.wikipedia.org/wiki/Payment_Card_Industry_Data_Security_Standard",[40],[8059],{"type":29,"value":8060},"PCI Compliance",{"type":29,"value":8062},", while still allowing for a fair amount of customizability, some of which we'll explore shortly.",{"type":23,"tag":32,"props":8064,"children":8065},{},[8066,8068,8075,8077,8083],{"type":29,"value":8067},"Braintree offers a ",{"type":23,"tag":36,"props":8069,"children":8072},{"href":8070,"rel":8071},"https://developers.braintreepayments.com/guides/client-sdk/setup/javascript/v2#install-it-with-npm",[40],[8073],{"type":29,"value":8074},"variety of options",{"type":29,"value":8076}," for integrating the library with your application. For the below, we assume we have a ",{"type":23,"tag":147,"props":8078,"children":8080},{"className":8079},[],[8081],{"type":29,"value":8082},"braintree",{"type":29,"value":8084}," object in global scope:",{"type":23,"tag":141,"props":8086,"children":8090},{"className":8087,"code":8088,"language":8089,"meta":8,"style":8},"language-javascript shiki shiki-themes github-light github-dark","        /**\n         * Request client token and initialize braintree payment library.\n         */\n    setupBraintree:function(){\n        $.ajax({\n            type:'GET',\n            dataType:'json',\n            url:'/payment/token/'\n        }).done(function(res){\n            braintree.setup(res.token, \"dropin\", {\n                container:$('#braintreeContainer')[0],\n                onReady:paymentMethodReady,\n                onPaymentMethodReceived:paymentMethodReceived,\n                onError:paymentMethodError\n            });\n        }.bind(this)).fail(function(jqXHR){\n            console.log(jqXHR, jqXHR.responseJSON || jqXHR.responseText);\n        });\n    }\n","javascript",[8091],{"type":23,"tag":147,"props":8092,"children":8093},{"__ignoreMap":8},[8094,8102,8110,8118,8140,8158,8174,8191,8204,8239,8267,8303,8311,8319,8327,8335,8388,8416,8424],{"type":23,"tag":151,"props":8095,"children":8096},{"class":153,"line":154},[8097],{"type":23,"tag":151,"props":8098,"children":8099},{"style":324},[8100],{"type":29,"value":8101},"        /**\n",{"type":23,"tag":151,"props":8103,"children":8104},{"class":153,"line":180},[8105],{"type":23,"tag":151,"props":8106,"children":8107},{"style":324},[8108],{"type":29,"value":8109},"         * Request client token and initialize braintree payment library.\n",{"type":23,"tag":151,"props":8111,"children":8112},{"class":153,"line":190},[8113],{"type":23,"tag":151,"props":8114,"children":8115},{"style":324},[8116],{"type":29,"value":8117},"         */\n",{"type":23,"tag":151,"props":8119,"children":8120},{"class":153,"line":220},[8121,8126,8130,8135],{"type":23,"tag":151,"props":8122,"children":8123},{"style":199},[8124],{"type":29,"value":8125},"    setupBraintree",{"type":23,"tag":151,"props":8127,"children":8128},{"style":164},[8129],{"type":29,"value":4539},{"type":23,"tag":151,"props":8131,"children":8132},{"style":158},[8133],{"type":29,"value":8134},"function",{"type":23,"tag":151,"props":8136,"children":8137},{"style":164},[8138],{"type":29,"value":8139},"(){\n",{"type":23,"tag":151,"props":8141,"children":8142},{"class":153,"line":240},[8143,8148,8153],{"type":23,"tag":151,"props":8144,"children":8145},{"style":164},[8146],{"type":29,"value":8147},"        $.",{"type":23,"tag":151,"props":8149,"children":8150},{"style":199},[8151],{"type":29,"value":8152},"ajax",{"type":23,"tag":151,"props":8154,"children":8155},{"style":164},[8156],{"type":29,"value":8157},"({\n",{"type":23,"tag":151,"props":8159,"children":8160},{"class":153,"line":254},[8161,8166,8170],{"type":23,"tag":151,"props":8162,"children":8163},{"style":164},[8164],{"type":29,"value":8165},"            type:",{"type":23,"tag":151,"props":8167,"children":8168},{"style":285},[8169],{"type":29,"value":1958},{"type":23,"tag":151,"props":8171,"children":8172},{"style":164},[8173],{"type":29,"value":1805},{"type":23,"tag":151,"props":8175,"children":8176},{"class":153,"line":263},[8177,8182,8187],{"type":23,"tag":151,"props":8178,"children":8179},{"style":164},[8180],{"type":29,"value":8181},"            dataType:",{"type":23,"tag":151,"props":8183,"children":8184},{"style":285},[8185],{"type":29,"value":8186},"'json'",{"type":23,"tag":151,"props":8188,"children":8189},{"style":164},[8190],{"type":29,"value":1805},{"type":23,"tag":151,"props":8192,"children":8193},{"class":153,"line":272},[8194,8199],{"type":23,"tag":151,"props":8195,"children":8196},{"style":164},[8197],{"type":29,"value":8198},"            url:",{"type":23,"tag":151,"props":8200,"children":8201},{"style":285},[8202],{"type":29,"value":8203},"'/payment/token/'\n",{"type":23,"tag":151,"props":8205,"children":8206},{"class":153,"line":296},[8207,8212,8217,8221,8225,8229,8234],{"type":23,"tag":151,"props":8208,"children":8209},{"style":164},[8210],{"type":29,"value":8211},"        }).",{"type":23,"tag":151,"props":8213,"children":8214},{"style":199},[8215],{"type":29,"value":8216},"done",{"type":23,"tag":151,"props":8218,"children":8219},{"style":164},[8220],{"type":29,"value":207},{"type":23,"tag":151,"props":8222,"children":8223},{"style":158},[8224],{"type":29,"value":8134},{"type":23,"tag":151,"props":8226,"children":8227},{"style":164},[8228],{"type":29,"value":207},{"type":23,"tag":151,"props":8230,"children":8231},{"style":764},[8232],{"type":29,"value":8233},"res",{"type":23,"tag":151,"props":8235,"children":8236},{"style":164},[8237],{"type":29,"value":8238},"){\n",{"type":23,"tag":151,"props":8240,"children":8241},{"class":153,"line":320},[8242,8247,8252,8257,8262],{"type":23,"tag":151,"props":8243,"children":8244},{"style":164},[8245],{"type":29,"value":8246},"            braintree.",{"type":23,"tag":151,"props":8248,"children":8249},{"style":199},[8250],{"type":29,"value":8251},"setup",{"type":23,"tag":151,"props":8253,"children":8254},{"style":164},[8255],{"type":29,"value":8256},"(res.token, ",{"type":23,"tag":151,"props":8258,"children":8259},{"style":285},[8260],{"type":29,"value":8261},"\"dropin\"",{"type":23,"tag":151,"props":8263,"children":8264},{"style":164},[8265],{"type":29,"value":8266},", {\n",{"type":23,"tag":151,"props":8268,"children":8269},{"class":153,"line":330},[8270,8275,8279,8283,8288,8293,8298],{"type":23,"tag":151,"props":8271,"children":8272},{"style":164},[8273],{"type":29,"value":8274},"                container:",{"type":23,"tag":151,"props":8276,"children":8277},{"style":199},[8278],{"type":29,"value":5255},{"type":23,"tag":151,"props":8280,"children":8281},{"style":164},[8282],{"type":29,"value":207},{"type":23,"tag":151,"props":8284,"children":8285},{"style":285},[8286],{"type":29,"value":8287},"'#braintreeContainer'",{"type":23,"tag":151,"props":8289,"children":8290},{"style":164},[8291],{"type":29,"value":8292},")[",{"type":23,"tag":151,"props":8294,"children":8295},{"style":224},[8296],{"type":29,"value":8297},"0",{"type":23,"tag":151,"props":8299,"children":8300},{"style":164},[8301],{"type":29,"value":8302},"],\n",{"type":23,"tag":151,"props":8304,"children":8305},{"class":153,"line":901},[8306],{"type":23,"tag":151,"props":8307,"children":8308},{"style":164},[8309],{"type":29,"value":8310},"                onReady:paymentMethodReady,\n",{"type":23,"tag":151,"props":8312,"children":8313},{"class":153,"line":922},[8314],{"type":23,"tag":151,"props":8315,"children":8316},{"style":164},[8317],{"type":29,"value":8318},"                onPaymentMethodReceived:paymentMethodReceived,\n",{"type":23,"tag":151,"props":8320,"children":8321},{"class":153,"line":930},[8322],{"type":23,"tag":151,"props":8323,"children":8324},{"style":164},[8325],{"type":29,"value":8326},"                onError:paymentMethodError\n",{"type":23,"tag":151,"props":8328,"children":8329},{"class":153,"line":971},[8330],{"type":23,"tag":151,"props":8331,"children":8332},{"style":164},[8333],{"type":29,"value":8334},"            });\n",{"type":23,"tag":151,"props":8336,"children":8337},{"class":153,"line":992},[8338,8343,8348,8352,8357,8362,8367,8371,8375,8379,8384],{"type":23,"tag":151,"props":8339,"children":8340},{"style":164},[8341],{"type":29,"value":8342},"        }.",{"type":23,"tag":151,"props":8344,"children":8345},{"style":199},[8346],{"type":29,"value":8347},"bind",{"type":23,"tag":151,"props":8349,"children":8350},{"style":164},[8351],{"type":29,"value":207},{"type":23,"tag":151,"props":8353,"children":8354},{"style":224},[8355],{"type":29,"value":8356},"this",{"type":23,"tag":151,"props":8358,"children":8359},{"style":164},[8360],{"type":29,"value":8361},")).",{"type":23,"tag":151,"props":8363,"children":8364},{"style":199},[8365],{"type":29,"value":8366},"fail",{"type":23,"tag":151,"props":8368,"children":8369},{"style":164},[8370],{"type":29,"value":207},{"type":23,"tag":151,"props":8372,"children":8373},{"style":158},[8374],{"type":29,"value":8134},{"type":23,"tag":151,"props":8376,"children":8377},{"style":164},[8378],{"type":29,"value":207},{"type":23,"tag":151,"props":8380,"children":8381},{"style":764},[8382],{"type":29,"value":8383},"jqXHR",{"type":23,"tag":151,"props":8385,"children":8386},{"style":164},[8387],{"type":29,"value":8238},{"type":23,"tag":151,"props":8389,"children":8390},{"class":153,"line":1000},[8391,8396,8401,8406,8411],{"type":23,"tag":151,"props":8392,"children":8393},{"style":164},[8394],{"type":29,"value":8395},"            console.",{"type":23,"tag":151,"props":8397,"children":8398},{"style":199},[8399],{"type":29,"value":8400},"log",{"type":23,"tag":151,"props":8402,"children":8403},{"style":164},[8404],{"type":29,"value":8405},"(jqXHR, jqXHR.responseJSON ",{"type":23,"tag":151,"props":8407,"children":8408},{"style":158},[8409],{"type":29,"value":8410},"||",{"type":23,"tag":151,"props":8412,"children":8413},{"style":164},[8414],{"type":29,"value":8415}," jqXHR.responseText);\n",{"type":23,"tag":151,"props":8417,"children":8418},{"class":153,"line":1025},[8419],{"type":23,"tag":151,"props":8420,"children":8421},{"style":164},[8422],{"type":29,"value":8423},"        });\n",{"type":23,"tag":151,"props":8425,"children":8426},{"class":153,"line":1046},[8427],{"type":23,"tag":151,"props":8428,"children":8429},{"style":164},[8430],{"type":29,"value":1859},{"type":23,"tag":32,"props":8432,"children":8433},{},[8434],{"type":29,"value":8435},"Here, we request the client token and, as soon as we've received it from the server, initialize the braintree library, telling it where to place the 'dropin' iframe, and specifying a number of other options, which we'll return to shortly.",{"type":23,"tag":32,"props":8437,"children":8438},{},[8439,8441,8447],{"type":29,"value":8440},"As we need a node for the braintree iframe to live in, which you'll notice in the above code we've given the id of ",{"type":23,"tag":147,"props":8442,"children":8444},{"className":8443},[],[8445],{"type":29,"value":8446},"braintreeContainer",{"type":29,"value":8448},", a sparse example of the markup might look like:",{"type":23,"tag":141,"props":8450,"children":8454},{"className":8451,"code":8452,"language":8453,"meta":8,"style":8},"language-html shiki shiki-themes github-light github-dark","    \u003Cbody>\n\n        \u003Cform>\n            \u003Cdiv id=\"braintreeContainer\">\u003C/div>\n\n            \u003Cbutton type=\"submit\">Pay Now\u003C/button>\n        \u003C/form>\n\n    \u003C/body>\n","html",[8455],{"type":23,"tag":147,"props":8456,"children":8457},{"__ignoreMap":8},[8458,8477,8484,8501,8541,8548,8587,8603,8610],{"type":23,"tag":151,"props":8459,"children":8460},{"class":153,"line":154},[8461,8466,8472],{"type":23,"tag":151,"props":8462,"children":8463},{"style":164},[8464],{"type":29,"value":8465},"    \u003C",{"type":23,"tag":151,"props":8467,"children":8469},{"style":8468},"--shiki-default:#22863A;--shiki-dark:#85E89D",[8470],{"type":29,"value":8471},"body",{"type":23,"tag":151,"props":8473,"children":8474},{"style":164},[8475],{"type":29,"value":8476},">\n",{"type":23,"tag":151,"props":8478,"children":8479},{"class":153,"line":180},[8480],{"type":23,"tag":151,"props":8481,"children":8482},{"emptyLinePlaceholder":184},[8483],{"type":29,"value":187},{"type":23,"tag":151,"props":8485,"children":8486},{"class":153,"line":190},[8487,8492,8497],{"type":23,"tag":151,"props":8488,"children":8489},{"style":164},[8490],{"type":29,"value":8491},"        \u003C",{"type":23,"tag":151,"props":8493,"children":8494},{"style":8468},[8495],{"type":29,"value":8496},"form",{"type":23,"tag":151,"props":8498,"children":8499},{"style":164},[8500],{"type":29,"value":8476},{"type":23,"tag":151,"props":8502,"children":8503},{"class":153,"line":220},[8504,8509,8514,8519,8523,8528,8533,8537],{"type":23,"tag":151,"props":8505,"children":8506},{"style":164},[8507],{"type":29,"value":8508},"            \u003C",{"type":23,"tag":151,"props":8510,"children":8511},{"style":8468},[8512],{"type":29,"value":8513},"div",{"type":23,"tag":151,"props":8515,"children":8516},{"style":199},[8517],{"type":29,"value":8518}," id",{"type":23,"tag":151,"props":8520,"children":8521},{"style":164},[8522],{"type":29,"value":544},{"type":23,"tag":151,"props":8524,"children":8525},{"style":285},[8526],{"type":29,"value":8527},"\"braintreeContainer\"",{"type":23,"tag":151,"props":8529,"children":8530},{"style":164},[8531],{"type":29,"value":8532},">\u003C/",{"type":23,"tag":151,"props":8534,"children":8535},{"style":8468},[8536],{"type":29,"value":8513},{"type":23,"tag":151,"props":8538,"children":8539},{"style":164},[8540],{"type":29,"value":8476},{"type":23,"tag":151,"props":8542,"children":8543},{"class":153,"line":240},[8544],{"type":23,"tag":151,"props":8545,"children":8546},{"emptyLinePlaceholder":184},[8547],{"type":29,"value":187},{"type":23,"tag":151,"props":8549,"children":8550},{"class":153,"line":254},[8551,8555,8560,8565,8569,8574,8579,8583],{"type":23,"tag":151,"props":8552,"children":8553},{"style":164},[8554],{"type":29,"value":8508},{"type":23,"tag":151,"props":8556,"children":8557},{"style":8468},[8558],{"type":29,"value":8559},"button",{"type":23,"tag":151,"props":8561,"children":8562},{"style":199},[8563],{"type":29,"value":8564}," type",{"type":23,"tag":151,"props":8566,"children":8567},{"style":164},[8568],{"type":29,"value":544},{"type":23,"tag":151,"props":8570,"children":8571},{"style":285},[8572],{"type":29,"value":8573},"\"submit\"",{"type":23,"tag":151,"props":8575,"children":8576},{"style":164},[8577],{"type":29,"value":8578},">Pay Now\u003C/",{"type":23,"tag":151,"props":8580,"children":8581},{"style":8468},[8582],{"type":29,"value":8559},{"type":23,"tag":151,"props":8584,"children":8585},{"style":164},[8586],{"type":29,"value":8476},{"type":23,"tag":151,"props":8588,"children":8589},{"class":153,"line":263},[8590,8595,8599],{"type":23,"tag":151,"props":8591,"children":8592},{"style":164},[8593],{"type":29,"value":8594},"        \u003C/",{"type":23,"tag":151,"props":8596,"children":8597},{"style":8468},[8598],{"type":29,"value":8496},{"type":23,"tag":151,"props":8600,"children":8601},{"style":164},[8602],{"type":29,"value":8476},{"type":23,"tag":151,"props":8604,"children":8605},{"class":153,"line":272},[8606],{"type":23,"tag":151,"props":8607,"children":8608},{"emptyLinePlaceholder":184},[8609],{"type":29,"value":187},{"type":23,"tag":151,"props":8611,"children":8612},{"class":153,"line":296},[8613,8618,8622],{"type":23,"tag":151,"props":8614,"children":8615},{"style":164},[8616],{"type":29,"value":8617},"    \u003C/",{"type":23,"tag":151,"props":8619,"children":8620},{"style":8468},[8621],{"type":29,"value":8471},{"type":23,"tag":151,"props":8623,"children":8624},{"style":164},[8625],{"type":29,"value":8476},{"type":23,"tag":32,"props":8627,"children":8628},{},[8629],{"type":29,"value":8630},"Notice the 'Pay Now' button - we'll need to use it to trigger submission of the containing form (which in turn will trigger submission/processing of the input within the braintree iframe). Notice also that we don't specify any form action - we'll be submitting our payment details by ajax, but we could instead specify an action, and send needed details via hidden fields in the form.",{"type":23,"tag":32,"props":8632,"children":8633},{},[8634],{"type":29,"value":8635},"At this point, if those additional options in the braintree.setup pointed to real functions, we would see something like:",{"type":23,"tag":32,"props":8637,"children":8638},{},[8639],{"type":23,"tag":419,"props":8640,"children":8643},{"alt":8641,"src":8642},"braintree client example","assets/images/bt_ex_1.jpg",[],{"type":23,"tag":24,"props":8645,"children":8647},{"id":8646},"client-options",[8648],{"type":29,"value":8649},"Client Options",{"type":23,"tag":32,"props":8651,"children":8652},{},[8653,8655,8662,8664,8670],{"type":29,"value":8654},"Let's take a closer look at those ",{"type":23,"tag":36,"props":8656,"children":8659},{"href":8657,"rel":8658},"https://developers.braintreepayments.com/reference/client-reference/javascript/v2/configuration#setup-method-options",[40],[8660],{"type":29,"value":8661},"setup options",{"type":29,"value":8663},", then. Besides the obvious ",{"type":23,"tag":147,"props":8665,"children":8667},{"className":8666},[],[8668],{"type":29,"value":8669},"container",{"type":29,"value":8671}," option, we have three more: onReady, onPaymentMethodReceived, and onError, which we supply with callback functions. The Braintree documentation adequately explains each of them (and a number of others we aren't taking a look at today, such as an option to enable CORS requests), so let's instead take at what other things might do in the specified functions.",{"type":23,"tag":141,"props":8673,"children":8675},{"className":8087,"code":8674,"language":8089,"meta":8,"style":8},"    /**\n     * As this function informs us that the braintree setup is complete, if we were hiding the braintree container node\n     * or displaying some other dialog while it loaded, we could now safely reveal it to the user.\n     * @param {object} integration An object containing the teardown, paypal and deviceData objects, as noted\n     * in the braintree docs.\n     */\n    paymentMethodReady:function(integration){\n        // e.g.\n        $('#braintreeContainer').show();\n    }\n    /**\n     * Called by the braintree js SDK once the payment method has been received - that is, once the user has selected\n     * their paypal account or entered their credit card details.\n     * @param {object} recv Contains the following:\n     * nonce: The payment method nonce.\n     * type: A string representing the type of payment method generated; either 'CreditCard' or 'PayPalAccount'.\n     * details: Additional details. See https://developers.braintreepayments.com/reference/client-reference/javascript/v2/configuration#onpaymentmethodreceived-details-object\n     */\n    paymentMethodReceived:function(recv){\n        // We could be submitting information to our server via the form, and add information we need submitted as hidden\n        // fields on the form, but we can also submit the information we want to the server within this callback via ajax.\n        var details = {otherDetails:{/* object containing any other info we want to send up */, payment:recv};\n\n        $.ajax({\n            url:'/payment/process/',\n            type:'POST',\n            dataType:'json',\n            contentType:'application/json',\n            data:JSON.stringify(details)\n        }).done(function(res){\n            // Do something with the response from your server.\n        }.bind(this)).fail(function(jqXHR){\n            var res = jqXHR.responseJSON || jqXHR.responseText;\n            console.log(res);\n        }\n    }\n\n    /**\n     * On payment method error, this callback will receive a detail object, as noted in the setup options\n     * documentation, which gives us some idea as to what error occurred, allowing us to choose how to \n     * respond to it or inform the client about it.\n     * @param {object} detail\n     */\n    paymentMethodError:function(detail){\n        console.log(detail);\n\n        if (detail.type == \"VALIDATION\"){\n            // Potentially display a notification to your user that validation failed.\n            return;\n        }\n\n        // Potentially display or log that another type of error occurred.\n    }\n",[8676],{"type":23,"tag":147,"props":8677,"children":8678},{"__ignoreMap":8},[8679,8687,8695,8703,8731,8739,8747,8776,8784,8815,8822,8829,8837,8845,8870,8878,8886,8894,8901,8930,8938,8946,8978,8985,9000,9016,9032,9047,9064,9091,9122,9130,9177,9208,9224,9232,9239,9246,9253,9261,9269,9277,9297,9304,9329,9346,9353,9380,9388,9401,9409,9417,9426],{"type":23,"tag":151,"props":8680,"children":8681},{"class":153,"line":154},[8682],{"type":23,"tag":151,"props":8683,"children":8684},{"style":324},[8685],{"type":29,"value":8686},"    /**\n",{"type":23,"tag":151,"props":8688,"children":8689},{"class":153,"line":180},[8690],{"type":23,"tag":151,"props":8691,"children":8692},{"style":324},[8693],{"type":29,"value":8694},"     * As this function informs us that the braintree setup is complete, if we were hiding the braintree container node\n",{"type":23,"tag":151,"props":8696,"children":8697},{"class":153,"line":190},[8698],{"type":23,"tag":151,"props":8699,"children":8700},{"style":324},[8701],{"type":29,"value":8702},"     * or displaying some other dialog while it loaded, we could now safely reveal it to the user.\n",{"type":23,"tag":151,"props":8704,"children":8705},{"class":153,"line":220},[8706,8711,8716,8721,8726],{"type":23,"tag":151,"props":8707,"children":8708},{"style":324},[8709],{"type":29,"value":8710},"     * ",{"type":23,"tag":151,"props":8712,"children":8713},{"style":158},[8714],{"type":29,"value":8715},"@param",{"type":23,"tag":151,"props":8717,"children":8718},{"style":199},[8719],{"type":29,"value":8720}," {object}",{"type":23,"tag":151,"props":8722,"children":8723},{"style":164},[8724],{"type":29,"value":8725}," integration",{"type":23,"tag":151,"props":8727,"children":8728},{"style":324},[8729],{"type":29,"value":8730}," An object containing the teardown, paypal and deviceData objects, as noted\n",{"type":23,"tag":151,"props":8732,"children":8733},{"class":153,"line":240},[8734],{"type":23,"tag":151,"props":8735,"children":8736},{"style":324},[8737],{"type":29,"value":8738},"     * in the braintree docs.\n",{"type":23,"tag":151,"props":8740,"children":8741},{"class":153,"line":254},[8742],{"type":23,"tag":151,"props":8743,"children":8744},{"style":324},[8745],{"type":29,"value":8746},"     */\n",{"type":23,"tag":151,"props":8748,"children":8749},{"class":153,"line":263},[8750,8755,8759,8763,8767,8772],{"type":23,"tag":151,"props":8751,"children":8752},{"style":199},[8753],{"type":29,"value":8754},"    paymentMethodReady",{"type":23,"tag":151,"props":8756,"children":8757},{"style":164},[8758],{"type":29,"value":4539},{"type":23,"tag":151,"props":8760,"children":8761},{"style":158},[8762],{"type":29,"value":8134},{"type":23,"tag":151,"props":8764,"children":8765},{"style":164},[8766],{"type":29,"value":207},{"type":23,"tag":151,"props":8768,"children":8769},{"style":764},[8770],{"type":29,"value":8771},"integration",{"type":23,"tag":151,"props":8773,"children":8774},{"style":164},[8775],{"type":29,"value":8238},{"type":23,"tag":151,"props":8777,"children":8778},{"class":153,"line":272},[8779],{"type":23,"tag":151,"props":8780,"children":8781},{"style":324},[8782],{"type":29,"value":8783},"        // e.g.\n",{"type":23,"tag":151,"props":8785,"children":8786},{"class":153,"line":296},[8787,8792,8796,8800,8805,8810],{"type":23,"tag":151,"props":8788,"children":8789},{"style":199},[8790],{"type":29,"value":8791},"        $",{"type":23,"tag":151,"props":8793,"children":8794},{"style":164},[8795],{"type":29,"value":207},{"type":23,"tag":151,"props":8797,"children":8798},{"style":285},[8799],{"type":29,"value":8287},{"type":23,"tag":151,"props":8801,"children":8802},{"style":164},[8803],{"type":29,"value":8804},").",{"type":23,"tag":151,"props":8806,"children":8807},{"style":199},[8808],{"type":29,"value":8809},"show",{"type":23,"tag":151,"props":8811,"children":8812},{"style":164},[8813],{"type":29,"value":8814},"();\n",{"type":23,"tag":151,"props":8816,"children":8817},{"class":153,"line":320},[8818],{"type":23,"tag":151,"props":8819,"children":8820},{"style":164},[8821],{"type":29,"value":1859},{"type":23,"tag":151,"props":8823,"children":8824},{"class":153,"line":330},[8825],{"type":23,"tag":151,"props":8826,"children":8827},{"style":324},[8828],{"type":29,"value":8686},{"type":23,"tag":151,"props":8830,"children":8831},{"class":153,"line":901},[8832],{"type":23,"tag":151,"props":8833,"children":8834},{"style":324},[8835],{"type":29,"value":8836},"     * Called by the braintree js SDK once the payment method has been received - that is, once the user has selected\n",{"type":23,"tag":151,"props":8838,"children":8839},{"class":153,"line":922},[8840],{"type":23,"tag":151,"props":8841,"children":8842},{"style":324},[8843],{"type":29,"value":8844},"     * their paypal account or entered their credit card details.\n",{"type":23,"tag":151,"props":8846,"children":8847},{"class":153,"line":930},[8848,8852,8856,8860,8865],{"type":23,"tag":151,"props":8849,"children":8850},{"style":324},[8851],{"type":29,"value":8710},{"type":23,"tag":151,"props":8853,"children":8854},{"style":158},[8855],{"type":29,"value":8715},{"type":23,"tag":151,"props":8857,"children":8858},{"style":199},[8859],{"type":29,"value":8720},{"type":23,"tag":151,"props":8861,"children":8862},{"style":164},[8863],{"type":29,"value":8864}," recv",{"type":23,"tag":151,"props":8866,"children":8867},{"style":324},[8868],{"type":29,"value":8869}," Contains the following:\n",{"type":23,"tag":151,"props":8871,"children":8872},{"class":153,"line":971},[8873],{"type":23,"tag":151,"props":8874,"children":8875},{"style":324},[8876],{"type":29,"value":8877},"     * nonce: The payment method nonce.\n",{"type":23,"tag":151,"props":8879,"children":8880},{"class":153,"line":992},[8881],{"type":23,"tag":151,"props":8882,"children":8883},{"style":324},[8884],{"type":29,"value":8885},"     * type: A string representing the type of payment method generated; either 'CreditCard' or 'PayPalAccount'.\n",{"type":23,"tag":151,"props":8887,"children":8888},{"class":153,"line":1000},[8889],{"type":23,"tag":151,"props":8890,"children":8891},{"style":324},[8892],{"type":29,"value":8893},"     * details: Additional details. See https://developers.braintreepayments.com/reference/client-reference/javascript/v2/configuration#onpaymentmethodreceived-details-object\n",{"type":23,"tag":151,"props":8895,"children":8896},{"class":153,"line":1025},[8897],{"type":23,"tag":151,"props":8898,"children":8899},{"style":324},[8900],{"type":29,"value":8746},{"type":23,"tag":151,"props":8902,"children":8903},{"class":153,"line":1046},[8904,8909,8913,8917,8921,8926],{"type":23,"tag":151,"props":8905,"children":8906},{"style":199},[8907],{"type":29,"value":8908},"    paymentMethodReceived",{"type":23,"tag":151,"props":8910,"children":8911},{"style":164},[8912],{"type":29,"value":4539},{"type":23,"tag":151,"props":8914,"children":8915},{"style":158},[8916],{"type":29,"value":8134},{"type":23,"tag":151,"props":8918,"children":8919},{"style":164},[8920],{"type":29,"value":207},{"type":23,"tag":151,"props":8922,"children":8923},{"style":764},[8924],{"type":29,"value":8925},"recv",{"type":23,"tag":151,"props":8927,"children":8928},{"style":164},[8929],{"type":29,"value":8238},{"type":23,"tag":151,"props":8931,"children":8932},{"class":153,"line":1054},[8933],{"type":23,"tag":151,"props":8934,"children":8935},{"style":324},[8936],{"type":29,"value":8937},"        // We could be submitting information to our server via the form, and add information we need submitted as hidden\n",{"type":23,"tag":151,"props":8939,"children":8940},{"class":153,"line":1091},[8941],{"type":23,"tag":151,"props":8942,"children":8943},{"style":324},[8944],{"type":29,"value":8945},"        // fields on the form, but we can also submit the information we want to the server within this callback via ajax.\n",{"type":23,"tag":151,"props":8947,"children":8948},{"class":153,"line":1112},[8949,8954,8959,8963,8968,8973],{"type":23,"tag":151,"props":8950,"children":8951},{"style":158},[8952],{"type":29,"value":8953},"        var",{"type":23,"tag":151,"props":8955,"children":8956},{"style":164},[8957],{"type":29,"value":8958}," details ",{"type":23,"tag":151,"props":8960,"children":8961},{"style":158},[8962],{"type":29,"value":544},{"type":23,"tag":151,"props":8964,"children":8965},{"style":164},[8966],{"type":29,"value":8967}," {otherDetails:{",{"type":23,"tag":151,"props":8969,"children":8970},{"style":324},[8971],{"type":29,"value":8972},"/* object containing any other info we want to send up */",{"type":23,"tag":151,"props":8974,"children":8975},{"style":164},[8976],{"type":29,"value":8977},", payment:recv};\n",{"type":23,"tag":151,"props":8979,"children":8980},{"class":153,"line":1120},[8981],{"type":23,"tag":151,"props":8982,"children":8983},{"emptyLinePlaceholder":184},[8984],{"type":29,"value":187},{"type":23,"tag":151,"props":8986,"children":8987},{"class":153,"line":1129},[8988,8992,8996],{"type":23,"tag":151,"props":8989,"children":8990},{"style":164},[8991],{"type":29,"value":8147},{"type":23,"tag":151,"props":8993,"children":8994},{"style":199},[8995],{"type":29,"value":8152},{"type":23,"tag":151,"props":8997,"children":8998},{"style":164},[8999],{"type":29,"value":8157},{"type":23,"tag":151,"props":9001,"children":9002},{"class":153,"line":1137},[9003,9007,9012],{"type":23,"tag":151,"props":9004,"children":9005},{"style":164},[9006],{"type":29,"value":8198},{"type":23,"tag":151,"props":9008,"children":9009},{"style":285},[9010],{"type":29,"value":9011},"'/payment/process/'",{"type":23,"tag":151,"props":9013,"children":9014},{"style":164},[9015],{"type":29,"value":1805},{"type":23,"tag":151,"props":9017,"children":9018},{"class":153,"line":1146},[9019,9023,9028],{"type":23,"tag":151,"props":9020,"children":9021},{"style":164},[9022],{"type":29,"value":8165},{"type":23,"tag":151,"props":9024,"children":9025},{"style":285},[9026],{"type":29,"value":9027},"'POST'",{"type":23,"tag":151,"props":9029,"children":9030},{"style":164},[9031],{"type":29,"value":1805},{"type":23,"tag":151,"props":9033,"children":9034},{"class":153,"line":1155},[9035,9039,9043],{"type":23,"tag":151,"props":9036,"children":9037},{"style":164},[9038],{"type":29,"value":8181},{"type":23,"tag":151,"props":9040,"children":9041},{"style":285},[9042],{"type":29,"value":8186},{"type":23,"tag":151,"props":9044,"children":9045},{"style":164},[9046],{"type":29,"value":1805},{"type":23,"tag":151,"props":9048,"children":9049},{"class":153,"line":2034},[9050,9055,9060],{"type":23,"tag":151,"props":9051,"children":9052},{"style":164},[9053],{"type":29,"value":9054},"            contentType:",{"type":23,"tag":151,"props":9056,"children":9057},{"style":285},[9058],{"type":29,"value":9059},"'application/json'",{"type":23,"tag":151,"props":9061,"children":9062},{"style":164},[9063],{"type":29,"value":1805},{"type":23,"tag":151,"props":9065,"children":9066},{"class":153,"line":2043},[9067,9072,9077,9081,9086],{"type":23,"tag":151,"props":9068,"children":9069},{"style":164},[9070],{"type":29,"value":9071},"            data:",{"type":23,"tag":151,"props":9073,"children":9074},{"style":224},[9075],{"type":29,"value":9076},"JSON",{"type":23,"tag":151,"props":9078,"children":9079},{"style":164},[9080],{"type":29,"value":386},{"type":23,"tag":151,"props":9082,"children":9083},{"style":199},[9084],{"type":29,"value":9085},"stringify",{"type":23,"tag":151,"props":9087,"children":9088},{"style":164},[9089],{"type":29,"value":9090},"(details)\n",{"type":23,"tag":151,"props":9092,"children":9093},{"class":153,"line":2052},[9094,9098,9102,9106,9110,9114,9118],{"type":23,"tag":151,"props":9095,"children":9096},{"style":164},[9097],{"type":29,"value":8211},{"type":23,"tag":151,"props":9099,"children":9100},{"style":199},[9101],{"type":29,"value":8216},{"type":23,"tag":151,"props":9103,"children":9104},{"style":164},[9105],{"type":29,"value":207},{"type":23,"tag":151,"props":9107,"children":9108},{"style":158},[9109],{"type":29,"value":8134},{"type":23,"tag":151,"props":9111,"children":9112},{"style":164},[9113],{"type":29,"value":207},{"type":23,"tag":151,"props":9115,"children":9116},{"style":764},[9117],{"type":29,"value":8233},{"type":23,"tag":151,"props":9119,"children":9120},{"style":164},[9121],{"type":29,"value":8238},{"type":23,"tag":151,"props":9123,"children":9124},{"class":153,"line":2061},[9125],{"type":23,"tag":151,"props":9126,"children":9127},{"style":324},[9128],{"type":29,"value":9129},"            // Do something with the response from your server.\n",{"type":23,"tag":151,"props":9131,"children":9132},{"class":153,"line":2070},[9133,9137,9141,9145,9149,9153,9157,9161,9165,9169,9173],{"type":23,"tag":151,"props":9134,"children":9135},{"style":164},[9136],{"type":29,"value":8342},{"type":23,"tag":151,"props":9138,"children":9139},{"style":199},[9140],{"type":29,"value":8347},{"type":23,"tag":151,"props":9142,"children":9143},{"style":164},[9144],{"type":29,"value":207},{"type":23,"tag":151,"props":9146,"children":9147},{"style":224},[9148],{"type":29,"value":8356},{"type":23,"tag":151,"props":9150,"children":9151},{"style":164},[9152],{"type":29,"value":8361},{"type":23,"tag":151,"props":9154,"children":9155},{"style":199},[9156],{"type":29,"value":8366},{"type":23,"tag":151,"props":9158,"children":9159},{"style":164},[9160],{"type":29,"value":207},{"type":23,"tag":151,"props":9162,"children":9163},{"style":158},[9164],{"type":29,"value":8134},{"type":23,"tag":151,"props":9166,"children":9167},{"style":164},[9168],{"type":29,"value":207},{"type":23,"tag":151,"props":9170,"children":9171},{"style":764},[9172],{"type":29,"value":8383},{"type":23,"tag":151,"props":9174,"children":9175},{"style":164},[9176],{"type":29,"value":8238},{"type":23,"tag":151,"props":9178,"children":9179},{"class":153,"line":2079},[9180,9185,9190,9194,9199,9203],{"type":23,"tag":151,"props":9181,"children":9182},{"style":158},[9183],{"type":29,"value":9184},"            var",{"type":23,"tag":151,"props":9186,"children":9187},{"style":164},[9188],{"type":29,"value":9189}," res ",{"type":23,"tag":151,"props":9191,"children":9192},{"style":158},[9193],{"type":29,"value":544},{"type":23,"tag":151,"props":9195,"children":9196},{"style":164},[9197],{"type":29,"value":9198}," jqXHR.responseJSON ",{"type":23,"tag":151,"props":9200,"children":9201},{"style":158},[9202],{"type":29,"value":8410},{"type":23,"tag":151,"props":9204,"children":9205},{"style":164},[9206],{"type":29,"value":9207}," jqXHR.responseText;\n",{"type":23,"tag":151,"props":9209,"children":9210},{"class":153,"line":2088},[9211,9215,9219],{"type":23,"tag":151,"props":9212,"children":9213},{"style":164},[9214],{"type":29,"value":8395},{"type":23,"tag":151,"props":9216,"children":9217},{"style":199},[9218],{"type":29,"value":8400},{"type":23,"tag":151,"props":9220,"children":9221},{"style":164},[9222],{"type":29,"value":9223},"(res);\n",{"type":23,"tag":151,"props":9225,"children":9226},{"class":153,"line":2097},[9227],{"type":23,"tag":151,"props":9228,"children":9229},{"style":164},[9230],{"type":29,"value":9231},"        }\n",{"type":23,"tag":151,"props":9233,"children":9234},{"class":153,"line":2106},[9235],{"type":23,"tag":151,"props":9236,"children":9237},{"style":164},[9238],{"type":29,"value":1859},{"type":23,"tag":151,"props":9240,"children":9241},{"class":153,"line":2115},[9242],{"type":23,"tag":151,"props":9243,"children":9244},{"emptyLinePlaceholder":184},[9245],{"type":29,"value":187},{"type":23,"tag":151,"props":9247,"children":9248},{"class":153,"line":2124},[9249],{"type":23,"tag":151,"props":9250,"children":9251},{"style":324},[9252],{"type":29,"value":8686},{"type":23,"tag":151,"props":9254,"children":9255},{"class":153,"line":2133},[9256],{"type":23,"tag":151,"props":9257,"children":9258},{"style":324},[9259],{"type":29,"value":9260},"     * On payment method error, this callback will receive a detail object, as noted in the setup options\n",{"type":23,"tag":151,"props":9262,"children":9263},{"class":153,"line":2142},[9264],{"type":23,"tag":151,"props":9265,"children":9266},{"style":324},[9267],{"type":29,"value":9268},"     * documentation, which gives us some idea as to what error occurred, allowing us to choose how to \n",{"type":23,"tag":151,"props":9270,"children":9271},{"class":153,"line":2151},[9272],{"type":23,"tag":151,"props":9273,"children":9274},{"style":324},[9275],{"type":29,"value":9276},"     * respond to it or inform the client about it.\n",{"type":23,"tag":151,"props":9278,"children":9279},{"class":153,"line":2160},[9280,9284,9288,9292],{"type":23,"tag":151,"props":9281,"children":9282},{"style":324},[9283],{"type":29,"value":8710},{"type":23,"tag":151,"props":9285,"children":9286},{"style":158},[9287],{"type":29,"value":8715},{"type":23,"tag":151,"props":9289,"children":9290},{"style":199},[9291],{"type":29,"value":8720},{"type":23,"tag":151,"props":9293,"children":9294},{"style":164},[9295],{"type":29,"value":9296}," detail\n",{"type":23,"tag":151,"props":9298,"children":9299},{"class":153,"line":2169},[9300],{"type":23,"tag":151,"props":9301,"children":9302},{"style":324},[9303],{"type":29,"value":8746},{"type":23,"tag":151,"props":9305,"children":9306},{"class":153,"line":2178},[9307,9312,9316,9320,9325],{"type":23,"tag":151,"props":9308,"children":9309},{"style":164},[9310],{"type":29,"value":9311},"    paymentMethodError:",{"type":23,"tag":151,"props":9313,"children":9314},{"style":158},[9315],{"type":29,"value":8134},{"type":23,"tag":151,"props":9317,"children":9318},{"style":164},[9319],{"type":29,"value":207},{"type":23,"tag":151,"props":9321,"children":9322},{"style":764},[9323],{"type":29,"value":9324},"detail",{"type":23,"tag":151,"props":9326,"children":9327},{"style":164},[9328],{"type":29,"value":8238},{"type":23,"tag":151,"props":9330,"children":9331},{"class":153,"line":2192},[9332,9337,9341],{"type":23,"tag":151,"props":9333,"children":9334},{"style":164},[9335],{"type":29,"value":9336},"        console.",{"type":23,"tag":151,"props":9338,"children":9339},{"style":199},[9340],{"type":29,"value":8400},{"type":23,"tag":151,"props":9342,"children":9343},{"style":164},[9344],{"type":29,"value":9345},"(detail);\n",{"type":23,"tag":151,"props":9347,"children":9348},{"class":153,"line":2200},[9349],{"type":23,"tag":151,"props":9350,"children":9351},{"emptyLinePlaceholder":184},[9352],{"type":29,"value":187},{"type":23,"tag":151,"props":9354,"children":9355},{"class":153,"line":2229},[9356,9361,9366,9371,9376],{"type":23,"tag":151,"props":9357,"children":9358},{"style":158},[9359],{"type":29,"value":9360},"        if",{"type":23,"tag":151,"props":9362,"children":9363},{"style":164},[9364],{"type":29,"value":9365}," (detail.type ",{"type":23,"tag":151,"props":9367,"children":9368},{"style":158},[9369],{"type":29,"value":9370},"==",{"type":23,"tag":151,"props":9372,"children":9373},{"style":285},[9374],{"type":29,"value":9375}," \"VALIDATION\"",{"type":23,"tag":151,"props":9377,"children":9378},{"style":164},[9379],{"type":29,"value":8238},{"type":23,"tag":151,"props":9381,"children":9382},{"class":153,"line":5420},[9383],{"type":23,"tag":151,"props":9384,"children":9385},{"style":324},[9386],{"type":29,"value":9387},"            // Potentially display a notification to your user that validation failed.\n",{"type":23,"tag":151,"props":9389,"children":9391},{"class":153,"line":9390},49,[9392,9397],{"type":23,"tag":151,"props":9393,"children":9394},{"style":158},[9395],{"type":29,"value":9396},"            return",{"type":23,"tag":151,"props":9398,"children":9399},{"style":164},[9400],{"type":29,"value":4969},{"type":23,"tag":151,"props":9402,"children":9404},{"class":153,"line":9403},50,[9405],{"type":23,"tag":151,"props":9406,"children":9407},{"style":164},[9408],{"type":29,"value":9231},{"type":23,"tag":151,"props":9410,"children":9412},{"class":153,"line":9411},51,[9413],{"type":23,"tag":151,"props":9414,"children":9415},{"emptyLinePlaceholder":184},[9416],{"type":29,"value":187},{"type":23,"tag":151,"props":9418,"children":9420},{"class":153,"line":9419},52,[9421],{"type":23,"tag":151,"props":9422,"children":9423},{"style":324},[9424],{"type":29,"value":9425},"        // Potentially display or log that another type of error occurred.\n",{"type":23,"tag":151,"props":9427,"children":9429},{"class":153,"line":9428},53,[9430],{"type":23,"tag":151,"props":9431,"children":9432},{"style":164},[9433],{"type":29,"value":1859},{"type":23,"tag":24,"props":9435,"children":9437},{"id":9436},"perform-transaction",[9438],{"type":29,"value":9439},"Perform Transaction",{"type":23,"tag":32,"props":9441,"children":9442},{},[9443,9445,9451],{"type":29,"value":9444},"You'll notice that we specified an endpoint of ",{"type":23,"tag":147,"props":9446,"children":9448},{"className":9447},[],[9449],{"type":29,"value":9450},"/payment/process/",{"type":29,"value":9452}," in the paymentMethodReceived callback on the client-side. So, on the server side, let's define this endpoint and finally process our payment.",{"type":23,"tag":141,"props":9454,"children":9456},{"className":143,"code":9455,"language":12,"meta":8,"style":8},"import braintree\nimport json\n\nfrom django.views.decorators.http import require_http_methods\nfrom django.http import JsonResponse\n\n#from yourapp.models import SaleItem, TransactionRecord\n\n@require_http_methods(['POST'])\ndef perform_braintree_simple_sale(request):\n    \"\"\"\n    Perform one-time, immediate settlement sale transaction using\n    the payment nonce sent to us by the client.\n    On failure, return the failure details to the client.\n    \"\"\"\n    try:\n        details = json.loads(request.body)\n    except ValueError:\n        # Handle the error case where you fail to parse the request body\n        return JsonResponse(status=400)\n\n    payment_details = details['payment']\n\n    # Note that we get the value of the transaction from a database row - generally speaking,\n    # it's a bad idea to trust the client to define the transaction amount - otherwise, the client\n    # may alter the request to say, have themselves charged $1 for something you want to charge $100\n    # for, and if you don't validate this, you're out $99. Allowing the client to specify what 'item' \n    # they'd like to purchase is safer, however, as long as the server remains in control of setting and\n    # confirming the price.\n    item = SaleItem.objects.get(type=\"your-sale-item\")\n    sale = {\n        \"amount\": str(item.amount),\n        \"payment_method_nonce\": payment_details['nonce'],\n        \"descriptor\": {\"name\": \"BIZ*NAME\"},\n        \"options\": {\n            \"paypal\": {\"description\": \"BizName (bizname.foo)\"},\n            # Note that setting this to true indicates we want both authorization and immediate settlement of this charge\n            \"submit_for_settlement\": True \n        }\n    }\n\n    # Perform transaction, making the remote request to braintree\n    payment_result = braintree.Transaction.sale(sale)\n\n    if payment_result.is_success is not True:\n        if not hasattr(payment_result, 'transation'):\n            \"\"\"\n            If the transaction was not successfully processed by braintree,\n            the list of possible errors is staggeringly long:\n            @see https://developers.braintreepayments.com/reference/general/validation-errors/all/python#transaction\n            You'll likely want to iterate through the deep_errors list and handle them appropriately.\n            \"\"\"\n            return JsonResponse(status=500)\n        else:\n            \"\"\"\n            If the transacation was successfully processed by braintree, but was rejected by\n            the payment processor, you'll want to handle this rejection appropriately.\n            @see https://developers.braintreepayments.com/reference/general/processor-responses/settlement-responses\n            \"\"\"\n            return JsonResponse(status=500)\n\n    transaction = payment_result.transaction\n    if transaction.amount \u003C item.amount:\n        # Confirm you actually were paid as much as your item costs! If you don't submit for settlement,\n        # or only submit for partial settlement, you should skip this check.\n        return JsonResponse(status=500)\n\n    # Transaction successful - you can now proceed to create a new user, produce a download link for an item,\n    # or whatever else it is you need payment processing for.\n    # You can also now create a local reference for the transaction, in case you ever need to look it up for refund\n    # purposes, for example.\n    TransactionRecord.objects.create(item=item, amount=transaction.amount,\n                                     transaction_id=transaction.id, type=transaction.payment_instrument_type)\n\n    return JsonResponse(status=200)\n",[9457],{"type":23,"tag":147,"props":9458,"children":9459},{"__ignoreMap":8},[9460,9471,9483,9490,9509,9528,9535,9543,9550,9569,9585,9592,9600,9608,9616,9623,9634,9651,9666,9674,9703,9710,9736,9743,9751,9759,9767,9775,9783,9791,9826,9842,9863,9885,9915,9927,9957,9965,9986,9993,10000,10007,10015,10032,10039,10070,10100,10108,10116,10124,10132,10140,10147,10174,10187,10195,10204,10213,10222,10230,10258,10266,10284,10307,10316,10325,10353,10361,10370,10379,10388,10397,10434,10465,10473],{"type":23,"tag":151,"props":9461,"children":9462},{"class":153,"line":154},[9463,9467],{"type":23,"tag":151,"props":9464,"children":9465},{"style":158},[9466],{"type":29,"value":172},{"type":23,"tag":151,"props":9468,"children":9469},{"style":164},[9470],{"type":29,"value":7403},{"type":23,"tag":151,"props":9472,"children":9473},{"class":153,"line":180},[9474,9478],{"type":23,"tag":151,"props":9475,"children":9476},{"style":158},[9477],{"type":29,"value":172},{"type":23,"tag":151,"props":9479,"children":9480},{"style":164},[9481],{"type":29,"value":9482}," json\n",{"type":23,"tag":151,"props":9484,"children":9485},{"class":153,"line":190},[9486],{"type":23,"tag":151,"props":9487,"children":9488},{"emptyLinePlaceholder":184},[9489],{"type":29,"value":187},{"type":23,"tag":151,"props":9491,"children":9492},{"class":153,"line":220},[9493,9497,9501,9505],{"type":23,"tag":151,"props":9494,"children":9495},{"style":158},[9496],{"type":29,"value":161},{"type":23,"tag":151,"props":9498,"children":9499},{"style":164},[9500],{"type":29,"value":7583},{"type":23,"tag":151,"props":9502,"children":9503},{"style":158},[9504],{"type":29,"value":172},{"type":23,"tag":151,"props":9506,"children":9507},{"style":164},[9508],{"type":29,"value":7592},{"type":23,"tag":151,"props":9510,"children":9511},{"class":153,"line":240},[9512,9516,9520,9524],{"type":23,"tag":151,"props":9513,"children":9514},{"style":158},[9515],{"type":29,"value":161},{"type":23,"tag":151,"props":9517,"children":9518},{"style":164},[9519],{"type":29,"value":4558},{"type":23,"tag":151,"props":9521,"children":9522},{"style":158},[9523],{"type":29,"value":172},{"type":23,"tag":151,"props":9525,"children":9526},{"style":164},[9527],{"type":29,"value":7571},{"type":23,"tag":151,"props":9529,"children":9530},{"class":153,"line":254},[9531],{"type":23,"tag":151,"props":9532,"children":9533},{"emptyLinePlaceholder":184},[9534],{"type":29,"value":187},{"type":23,"tag":151,"props":9536,"children":9537},{"class":153,"line":263},[9538],{"type":23,"tag":151,"props":9539,"children":9540},{"style":324},[9541],{"type":29,"value":9542},"#from yourapp.models import SaleItem, TransactionRecord\n",{"type":23,"tag":151,"props":9544,"children":9545},{"class":153,"line":272},[9546],{"type":23,"tag":151,"props":9547,"children":9548},{"emptyLinePlaceholder":184},[9549],{"type":29,"value":187},{"type":23,"tag":151,"props":9551,"children":9552},{"class":153,"line":296},[9553,9557,9561,9565],{"type":23,"tag":151,"props":9554,"children":9555},{"style":199},[9556],{"type":29,"value":7607},{"type":23,"tag":151,"props":9558,"children":9559},{"style":164},[9560],{"type":29,"value":7612},{"type":23,"tag":151,"props":9562,"children":9563},{"style":285},[9564],{"type":29,"value":9027},{"type":23,"tag":151,"props":9566,"children":9567},{"style":164},[9568],{"type":29,"value":1963},{"type":23,"tag":151,"props":9570,"children":9571},{"class":153,"line":320},[9572,9576,9581],{"type":23,"tag":151,"props":9573,"children":9574},{"style":158},[9575],{"type":29,"value":794},{"type":23,"tag":151,"props":9577,"children":9578},{"style":199},[9579],{"type":29,"value":9580}," perform_braintree_simple_sale",{"type":23,"tag":151,"props":9582,"children":9583},{"style":164},[9584],{"type":29,"value":7637},{"type":23,"tag":151,"props":9586,"children":9587},{"class":153,"line":330},[9588],{"type":23,"tag":151,"props":9589,"children":9590},{"style":285},[9591],{"type":29,"value":7645},{"type":23,"tag":151,"props":9593,"children":9594},{"class":153,"line":901},[9595],{"type":23,"tag":151,"props":9596,"children":9597},{"style":285},[9598],{"type":29,"value":9599},"    Perform one-time, immediate settlement sale transaction using\n",{"type":23,"tag":151,"props":9601,"children":9602},{"class":153,"line":922},[9603],{"type":23,"tag":151,"props":9604,"children":9605},{"style":285},[9606],{"type":29,"value":9607},"    the payment nonce sent to us by the client.\n",{"type":23,"tag":151,"props":9609,"children":9610},{"class":153,"line":930},[9611],{"type":23,"tag":151,"props":9612,"children":9613},{"style":285},[9614],{"type":29,"value":9615},"    On failure, return the failure details to the client.\n",{"type":23,"tag":151,"props":9617,"children":9618},{"class":153,"line":971},[9619],{"type":23,"tag":151,"props":9620,"children":9621},{"style":285},[9622],{"type":29,"value":7645},{"type":23,"tag":151,"props":9624,"children":9625},{"class":153,"line":992},[9626,9630],{"type":23,"tag":151,"props":9627,"children":9628},{"style":158},[9629],{"type":29,"value":7668},{"type":23,"tag":151,"props":9631,"children":9632},{"style":164},[9633],{"type":29,"value":2226},{"type":23,"tag":151,"props":9635,"children":9636},{"class":153,"line":1000},[9637,9642,9646],{"type":23,"tag":151,"props":9638,"children":9639},{"style":164},[9640],{"type":29,"value":9641},"        details ",{"type":23,"tag":151,"props":9643,"children":9644},{"style":158},[9645],{"type":29,"value":544},{"type":23,"tag":151,"props":9647,"children":9648},{"style":164},[9649],{"type":29,"value":9650}," json.loads(request.body)\n",{"type":23,"tag":151,"props":9652,"children":9653},{"class":153,"line":1025},[9654,9658,9662],{"type":23,"tag":151,"props":9655,"children":9656},{"style":158},[9657],{"type":29,"value":7697},{"type":23,"tag":151,"props":9659,"children":9660},{"style":224},[9661],{"type":29,"value":7702},{"type":23,"tag":151,"props":9663,"children":9664},{"style":164},[9665],{"type":29,"value":2226},{"type":23,"tag":151,"props":9667,"children":9668},{"class":153,"line":1046},[9669],{"type":23,"tag":151,"props":9670,"children":9671},{"style":324},[9672],{"type":29,"value":9673},"        # Handle the error case where you fail to parse the request body\n",{"type":23,"tag":151,"props":9675,"children":9676},{"class":153,"line":1054},[9677,9681,9686,9690,9694,9699],{"type":23,"tag":151,"props":9678,"children":9679},{"style":158},[9680],{"type":29,"value":7720},{"type":23,"tag":151,"props":9682,"children":9683},{"style":164},[9684],{"type":29,"value":9685}," JsonResponse(",{"type":23,"tag":151,"props":9687,"children":9688},{"style":764},[9689],{"type":29,"value":7740},{"type":23,"tag":151,"props":9691,"children":9692},{"style":158},[9693],{"type":29,"value":544},{"type":23,"tag":151,"props":9695,"children":9696},{"style":224},[9697],{"type":29,"value":9698},"400",{"type":23,"tag":151,"props":9700,"children":9701},{"style":164},[9702],{"type":29,"value":293},{"type":23,"tag":151,"props":9704,"children":9705},{"class":153,"line":1091},[9706],{"type":23,"tag":151,"props":9707,"children":9708},{"emptyLinePlaceholder":184},[9709],{"type":29,"value":187},{"type":23,"tag":151,"props":9711,"children":9712},{"class":153,"line":1112},[9713,9718,9722,9727,9732],{"type":23,"tag":151,"props":9714,"children":9715},{"style":164},[9716],{"type":29,"value":9717},"    payment_details ",{"type":23,"tag":151,"props":9719,"children":9720},{"style":158},[9721],{"type":29,"value":544},{"type":23,"tag":151,"props":9723,"children":9724},{"style":164},[9725],{"type":29,"value":9726}," details[",{"type":23,"tag":151,"props":9728,"children":9729},{"style":285},[9730],{"type":29,"value":9731},"'payment'",{"type":23,"tag":151,"props":9733,"children":9734},{"style":164},[9735],{"type":29,"value":4410},{"type":23,"tag":151,"props":9737,"children":9738},{"class":153,"line":1120},[9739],{"type":23,"tag":151,"props":9740,"children":9741},{"emptyLinePlaceholder":184},[9742],{"type":29,"value":187},{"type":23,"tag":151,"props":9744,"children":9745},{"class":153,"line":1129},[9746],{"type":23,"tag":151,"props":9747,"children":9748},{"style":324},[9749],{"type":29,"value":9750},"    # Note that we get the value of the transaction from a database row - generally speaking,\n",{"type":23,"tag":151,"props":9752,"children":9753},{"class":153,"line":1137},[9754],{"type":23,"tag":151,"props":9755,"children":9756},{"style":324},[9757],{"type":29,"value":9758},"    # it's a bad idea to trust the client to define the transaction amount - otherwise, the client\n",{"type":23,"tag":151,"props":9760,"children":9761},{"class":153,"line":1146},[9762],{"type":23,"tag":151,"props":9763,"children":9764},{"style":324},[9765],{"type":29,"value":9766},"    # may alter the request to say, have themselves charged $1 for something you want to charge $100\n",{"type":23,"tag":151,"props":9768,"children":9769},{"class":153,"line":1155},[9770],{"type":23,"tag":151,"props":9771,"children":9772},{"style":324},[9773],{"type":29,"value":9774},"    # for, and if you don't validate this, you're out $99. Allowing the client to specify what 'item' \n",{"type":23,"tag":151,"props":9776,"children":9777},{"class":153,"line":2034},[9778],{"type":23,"tag":151,"props":9779,"children":9780},{"style":324},[9781],{"type":29,"value":9782},"    # they'd like to purchase is safer, however, as long as the server remains in control of setting and\n",{"type":23,"tag":151,"props":9784,"children":9785},{"class":153,"line":2043},[9786],{"type":23,"tag":151,"props":9787,"children":9788},{"style":324},[9789],{"type":29,"value":9790},"    # confirming the price.\n",{"type":23,"tag":151,"props":9792,"children":9793},{"class":153,"line":2052},[9794,9799,9803,9808,9813,9817,9822],{"type":23,"tag":151,"props":9795,"children":9796},{"style":164},[9797],{"type":29,"value":9798},"    item ",{"type":23,"tag":151,"props":9800,"children":9801},{"style":158},[9802],{"type":29,"value":544},{"type":23,"tag":151,"props":9804,"children":9805},{"style":164},[9806],{"type":29,"value":9807}," SaleItem.objects.get(",{"type":23,"tag":151,"props":9809,"children":9810},{"style":764},[9811],{"type":29,"value":9812},"type",{"type":23,"tag":151,"props":9814,"children":9815},{"style":158},[9816],{"type":29,"value":544},{"type":23,"tag":151,"props":9818,"children":9819},{"style":285},[9820],{"type":29,"value":9821},"\"your-sale-item\"",{"type":23,"tag":151,"props":9823,"children":9824},{"style":164},[9825],{"type":29,"value":293},{"type":23,"tag":151,"props":9827,"children":9828},{"class":153,"line":2061},[9829,9834,9838],{"type":23,"tag":151,"props":9830,"children":9831},{"style":164},[9832],{"type":29,"value":9833},"    sale ",{"type":23,"tag":151,"props":9835,"children":9836},{"style":158},[9837],{"type":29,"value":544},{"type":23,"tag":151,"props":9839,"children":9840},{"style":164},[9841],{"type":29,"value":3946},{"type":23,"tag":151,"props":9843,"children":9844},{"class":153,"line":2070},[9845,9850,9854,9858],{"type":23,"tag":151,"props":9846,"children":9847},{"style":285},[9848],{"type":29,"value":9849},"        \"amount\"",{"type":23,"tag":151,"props":9851,"children":9852},{"style":164},[9853],{"type":29,"value":232},{"type":23,"tag":151,"props":9855,"children":9856},{"style":224},[9857],{"type":29,"value":964},{"type":23,"tag":151,"props":9859,"children":9860},{"style":164},[9861],{"type":29,"value":9862},"(item.amount),\n",{"type":23,"tag":151,"props":9864,"children":9865},{"class":153,"line":2079},[9866,9871,9876,9881],{"type":23,"tag":151,"props":9867,"children":9868},{"style":285},[9869],{"type":29,"value":9870},"        \"payment_method_nonce\"",{"type":23,"tag":151,"props":9872,"children":9873},{"style":164},[9874],{"type":29,"value":9875},": payment_details[",{"type":23,"tag":151,"props":9877,"children":9878},{"style":285},[9879],{"type":29,"value":9880},"'nonce'",{"type":23,"tag":151,"props":9882,"children":9883},{"style":164},[9884],{"type":29,"value":8302},{"type":23,"tag":151,"props":9886,"children":9887},{"class":153,"line":2088},[9888,9893,9898,9902,9906,9911],{"type":23,"tag":151,"props":9889,"children":9890},{"style":285},[9891],{"type":29,"value":9892},"        \"descriptor\"",{"type":23,"tag":151,"props":9894,"children":9895},{"style":164},[9896],{"type":29,"value":9897},": {",{"type":23,"tag":151,"props":9899,"children":9900},{"style":285},[9901],{"type":29,"value":954},{"type":23,"tag":151,"props":9903,"children":9904},{"style":164},[9905],{"type":29,"value":232},{"type":23,"tag":151,"props":9907,"children":9908},{"style":285},[9909],{"type":29,"value":9910},"\"BIZ*NAME\"",{"type":23,"tag":151,"props":9912,"children":9913},{"style":164},[9914],{"type":29,"value":4218},{"type":23,"tag":151,"props":9916,"children":9917},{"class":153,"line":2097},[9918,9923],{"type":23,"tag":151,"props":9919,"children":9920},{"style":285},[9921],{"type":29,"value":9922},"        \"options\"",{"type":23,"tag":151,"props":9924,"children":9925},{"style":164},[9926],{"type":29,"value":3959},{"type":23,"tag":151,"props":9928,"children":9929},{"class":153,"line":2106},[9930,9935,9939,9944,9948,9953],{"type":23,"tag":151,"props":9931,"children":9932},{"style":285},[9933],{"type":29,"value":9934},"            \"paypal\"",{"type":23,"tag":151,"props":9936,"children":9937},{"style":164},[9938],{"type":29,"value":9897},{"type":23,"tag":151,"props":9940,"children":9941},{"style":285},[9942],{"type":29,"value":9943},"\"description\"",{"type":23,"tag":151,"props":9945,"children":9946},{"style":164},[9947],{"type":29,"value":232},{"type":23,"tag":151,"props":9949,"children":9950},{"style":285},[9951],{"type":29,"value":9952},"\"BizName (bizname.foo)\"",{"type":23,"tag":151,"props":9954,"children":9955},{"style":164},[9956],{"type":29,"value":4218},{"type":23,"tag":151,"props":9958,"children":9959},{"class":153,"line":2115},[9960],{"type":23,"tag":151,"props":9961,"children":9962},{"style":324},[9963],{"type":29,"value":9964},"            # Note that setting this to true indicates we want both authorization and immediate settlement of this charge\n",{"type":23,"tag":151,"props":9966,"children":9967},{"class":153,"line":2124},[9968,9973,9977,9982],{"type":23,"tag":151,"props":9969,"children":9970},{"style":285},[9971],{"type":29,"value":9972},"            \"submit_for_settlement\"",{"type":23,"tag":151,"props":9974,"children":9975},{"style":164},[9976],{"type":29,"value":232},{"type":23,"tag":151,"props":9978,"children":9979},{"style":224},[9980],{"type":29,"value":9981},"True",{"type":23,"tag":151,"props":9983,"children":9984},{"style":164},[9985],{"type":29,"value":2189},{"type":23,"tag":151,"props":9987,"children":9988},{"class":153,"line":2133},[9989],{"type":23,"tag":151,"props":9990,"children":9991},{"style":164},[9992],{"type":29,"value":9231},{"type":23,"tag":151,"props":9994,"children":9995},{"class":153,"line":2142},[9996],{"type":23,"tag":151,"props":9997,"children":9998},{"style":164},[9999],{"type":29,"value":1859},{"type":23,"tag":151,"props":10001,"children":10002},{"class":153,"line":2151},[10003],{"type":23,"tag":151,"props":10004,"children":10005},{"emptyLinePlaceholder":184},[10006],{"type":29,"value":187},{"type":23,"tag":151,"props":10008,"children":10009},{"class":153,"line":2160},[10010],{"type":23,"tag":151,"props":10011,"children":10012},{"style":324},[10013],{"type":29,"value":10014},"    # Perform transaction, making the remote request to braintree\n",{"type":23,"tag":151,"props":10016,"children":10017},{"class":153,"line":2169},[10018,10023,10027],{"type":23,"tag":151,"props":10019,"children":10020},{"style":164},[10021],{"type":29,"value":10022},"    payment_result ",{"type":23,"tag":151,"props":10024,"children":10025},{"style":158},[10026],{"type":29,"value":544},{"type":23,"tag":151,"props":10028,"children":10029},{"style":164},[10030],{"type":29,"value":10031}," braintree.Transaction.sale(sale)\n",{"type":23,"tag":151,"props":10033,"children":10034},{"class":153,"line":2178},[10035],{"type":23,"tag":151,"props":10036,"children":10037},{"emptyLinePlaceholder":184},[10038],{"type":29,"value":187},{"type":23,"tag":151,"props":10040,"children":10041},{"class":153,"line":2192},[10042,10047,10052,10057,10061,10066],{"type":23,"tag":151,"props":10043,"children":10044},{"style":158},[10045],{"type":29,"value":10046},"    if",{"type":23,"tag":151,"props":10048,"children":10049},{"style":164},[10050],{"type":29,"value":10051}," payment_result.is_success ",{"type":23,"tag":151,"props":10053,"children":10054},{"style":158},[10055],{"type":29,"value":10056},"is",{"type":23,"tag":151,"props":10058,"children":10059},{"style":158},[10060],{"type":29,"value":840},{"type":23,"tag":151,"props":10062,"children":10063},{"style":224},[10064],{"type":29,"value":10065}," True",{"type":23,"tag":151,"props":10067,"children":10068},{"style":164},[10069],{"type":29,"value":2226},{"type":23,"tag":151,"props":10071,"children":10072},{"class":153,"line":2200},[10073,10077,10081,10086,10091,10096],{"type":23,"tag":151,"props":10074,"children":10075},{"style":158},[10076],{"type":29,"value":9360},{"type":23,"tag":151,"props":10078,"children":10079},{"style":158},[10080],{"type":29,"value":840},{"type":23,"tag":151,"props":10082,"children":10083},{"style":224},[10084],{"type":29,"value":10085}," hasattr",{"type":23,"tag":151,"props":10087,"children":10088},{"style":164},[10089],{"type":29,"value":10090},"(payment_result, ",{"type":23,"tag":151,"props":10092,"children":10093},{"style":285},[10094],{"type":29,"value":10095},"'transation'",{"type":23,"tag":151,"props":10097,"children":10098},{"style":164},[10099],{"type":29,"value":217},{"type":23,"tag":151,"props":10101,"children":10102},{"class":153,"line":2229},[10103],{"type":23,"tag":151,"props":10104,"children":10105},{"style":285},[10106],{"type":29,"value":10107},"            \"\"\"\n",{"type":23,"tag":151,"props":10109,"children":10110},{"class":153,"line":5420},[10111],{"type":23,"tag":151,"props":10112,"children":10113},{"style":285},[10114],{"type":29,"value":10115},"            If the transaction was not successfully processed by braintree,\n",{"type":23,"tag":151,"props":10117,"children":10118},{"class":153,"line":9390},[10119],{"type":23,"tag":151,"props":10120,"children":10121},{"style":285},[10122],{"type":29,"value":10123},"            the list of possible errors is staggeringly long:\n",{"type":23,"tag":151,"props":10125,"children":10126},{"class":153,"line":9403},[10127],{"type":23,"tag":151,"props":10128,"children":10129},{"style":285},[10130],{"type":29,"value":10131},"            @see https://developers.braintreepayments.com/reference/general/validation-errors/all/python#transaction\n",{"type":23,"tag":151,"props":10133,"children":10134},{"class":153,"line":9411},[10135],{"type":23,"tag":151,"props":10136,"children":10137},{"style":285},[10138],{"type":29,"value":10139},"            You'll likely want to iterate through the deep_errors list and handle them appropriately.\n",{"type":23,"tag":151,"props":10141,"children":10142},{"class":153,"line":9419},[10143],{"type":23,"tag":151,"props":10144,"children":10145},{"style":285},[10146],{"type":29,"value":10107},{"type":23,"tag":151,"props":10148,"children":10149},{"class":153,"line":9428},[10150,10154,10158,10162,10166,10170],{"type":23,"tag":151,"props":10151,"children":10152},{"style":158},[10153],{"type":29,"value":9396},{"type":23,"tag":151,"props":10155,"children":10156},{"style":164},[10157],{"type":29,"value":9685},{"type":23,"tag":151,"props":10159,"children":10160},{"style":764},[10161],{"type":29,"value":7740},{"type":23,"tag":151,"props":10163,"children":10164},{"style":158},[10165],{"type":29,"value":544},{"type":23,"tag":151,"props":10167,"children":10168},{"style":224},[10169],{"type":29,"value":7749},{"type":23,"tag":151,"props":10171,"children":10172},{"style":164},[10173],{"type":29,"value":293},{"type":23,"tag":151,"props":10175,"children":10177},{"class":153,"line":10176},54,[10178,10183],{"type":23,"tag":151,"props":10179,"children":10180},{"style":158},[10181],{"type":29,"value":10182},"        else",{"type":23,"tag":151,"props":10184,"children":10185},{"style":164},[10186],{"type":29,"value":2226},{"type":23,"tag":151,"props":10188,"children":10190},{"class":153,"line":10189},55,[10191],{"type":23,"tag":151,"props":10192,"children":10193},{"style":285},[10194],{"type":29,"value":10107},{"type":23,"tag":151,"props":10196,"children":10198},{"class":153,"line":10197},56,[10199],{"type":23,"tag":151,"props":10200,"children":10201},{"style":285},[10202],{"type":29,"value":10203},"            If the transacation was successfully processed by braintree, but was rejected by\n",{"type":23,"tag":151,"props":10205,"children":10207},{"class":153,"line":10206},57,[10208],{"type":23,"tag":151,"props":10209,"children":10210},{"style":285},[10211],{"type":29,"value":10212},"            the payment processor, you'll want to handle this rejection appropriately.\n",{"type":23,"tag":151,"props":10214,"children":10216},{"class":153,"line":10215},58,[10217],{"type":23,"tag":151,"props":10218,"children":10219},{"style":285},[10220],{"type":29,"value":10221},"            @see https://developers.braintreepayments.com/reference/general/processor-responses/settlement-responses\n",{"type":23,"tag":151,"props":10223,"children":10225},{"class":153,"line":10224},59,[10226],{"type":23,"tag":151,"props":10227,"children":10228},{"style":285},[10229],{"type":29,"value":10107},{"type":23,"tag":151,"props":10231,"children":10233},{"class":153,"line":10232},60,[10234,10238,10242,10246,10250,10254],{"type":23,"tag":151,"props":10235,"children":10236},{"style":158},[10237],{"type":29,"value":9396},{"type":23,"tag":151,"props":10239,"children":10240},{"style":164},[10241],{"type":29,"value":9685},{"type":23,"tag":151,"props":10243,"children":10244},{"style":764},[10245],{"type":29,"value":7740},{"type":23,"tag":151,"props":10247,"children":10248},{"style":158},[10249],{"type":29,"value":544},{"type":23,"tag":151,"props":10251,"children":10252},{"style":224},[10253],{"type":29,"value":7749},{"type":23,"tag":151,"props":10255,"children":10256},{"style":164},[10257],{"type":29,"value":293},{"type":23,"tag":151,"props":10259,"children":10261},{"class":153,"line":10260},61,[10262],{"type":23,"tag":151,"props":10263,"children":10264},{"emptyLinePlaceholder":184},[10265],{"type":29,"value":187},{"type":23,"tag":151,"props":10267,"children":10269},{"class":153,"line":10268},62,[10270,10275,10279],{"type":23,"tag":151,"props":10271,"children":10272},{"style":164},[10273],{"type":29,"value":10274},"    transaction ",{"type":23,"tag":151,"props":10276,"children":10277},{"style":158},[10278],{"type":29,"value":544},{"type":23,"tag":151,"props":10280,"children":10281},{"style":164},[10282],{"type":29,"value":10283}," payment_result.transaction\n",{"type":23,"tag":151,"props":10285,"children":10287},{"class":153,"line":10286},63,[10288,10292,10297,10302],{"type":23,"tag":151,"props":10289,"children":10290},{"style":158},[10291],{"type":29,"value":10046},{"type":23,"tag":151,"props":10293,"children":10294},{"style":164},[10295],{"type":29,"value":10296}," transaction.amount ",{"type":23,"tag":151,"props":10298,"children":10299},{"style":158},[10300],{"type":29,"value":10301},"\u003C",{"type":23,"tag":151,"props":10303,"children":10304},{"style":164},[10305],{"type":29,"value":10306}," item.amount:\n",{"type":23,"tag":151,"props":10308,"children":10310},{"class":153,"line":10309},64,[10311],{"type":23,"tag":151,"props":10312,"children":10313},{"style":324},[10314],{"type":29,"value":10315},"        # Confirm you actually were paid as much as your item costs! If you don't submit for settlement,\n",{"type":23,"tag":151,"props":10317,"children":10319},{"class":153,"line":10318},65,[10320],{"type":23,"tag":151,"props":10321,"children":10322},{"style":324},[10323],{"type":29,"value":10324},"        # or only submit for partial settlement, you should skip this check.\n",{"type":23,"tag":151,"props":10326,"children":10328},{"class":153,"line":10327},66,[10329,10333,10337,10341,10345,10349],{"type":23,"tag":151,"props":10330,"children":10331},{"style":158},[10332],{"type":29,"value":7720},{"type":23,"tag":151,"props":10334,"children":10335},{"style":164},[10336],{"type":29,"value":9685},{"type":23,"tag":151,"props":10338,"children":10339},{"style":764},[10340],{"type":29,"value":7740},{"type":23,"tag":151,"props":10342,"children":10343},{"style":158},[10344],{"type":29,"value":544},{"type":23,"tag":151,"props":10346,"children":10347},{"style":224},[10348],{"type":29,"value":7749},{"type":23,"tag":151,"props":10350,"children":10351},{"style":164},[10352],{"type":29,"value":293},{"type":23,"tag":151,"props":10354,"children":10356},{"class":153,"line":10355},67,[10357],{"type":23,"tag":151,"props":10358,"children":10359},{"emptyLinePlaceholder":184},[10360],{"type":29,"value":187},{"type":23,"tag":151,"props":10362,"children":10364},{"class":153,"line":10363},68,[10365],{"type":23,"tag":151,"props":10366,"children":10367},{"style":324},[10368],{"type":29,"value":10369},"    # Transaction successful - you can now proceed to create a new user, produce a download link for an item,\n",{"type":23,"tag":151,"props":10371,"children":10373},{"class":153,"line":10372},69,[10374],{"type":23,"tag":151,"props":10375,"children":10376},{"style":324},[10377],{"type":29,"value":10378},"    # or whatever else it is you need payment processing for.\n",{"type":23,"tag":151,"props":10380,"children":10382},{"class":153,"line":10381},70,[10383],{"type":23,"tag":151,"props":10384,"children":10385},{"style":324},[10386],{"type":29,"value":10387},"    # You can also now create a local reference for the transaction, in case you ever need to look it up for refund\n",{"type":23,"tag":151,"props":10389,"children":10391},{"class":153,"line":10390},71,[10392],{"type":23,"tag":151,"props":10393,"children":10394},{"style":324},[10395],{"type":29,"value":10396},"    # purposes, for example.\n",{"type":23,"tag":151,"props":10398,"children":10400},{"class":153,"line":10399},72,[10401,10406,10411,10415,10420,10425,10429],{"type":23,"tag":151,"props":10402,"children":10403},{"style":164},[10404],{"type":29,"value":10405},"    TransactionRecord.objects.create(",{"type":23,"tag":151,"props":10407,"children":10408},{"style":764},[10409],{"type":29,"value":10410},"item",{"type":23,"tag":151,"props":10412,"children":10413},{"style":158},[10414],{"type":29,"value":544},{"type":23,"tag":151,"props":10416,"children":10417},{"style":164},[10418],{"type":29,"value":10419},"item, ",{"type":23,"tag":151,"props":10421,"children":10422},{"style":764},[10423],{"type":29,"value":10424},"amount",{"type":23,"tag":151,"props":10426,"children":10427},{"style":158},[10428],{"type":29,"value":544},{"type":23,"tag":151,"props":10430,"children":10431},{"style":164},[10432],{"type":29,"value":10433},"transaction.amount,\n",{"type":23,"tag":151,"props":10435,"children":10437},{"class":153,"line":10436},73,[10438,10443,10447,10452,10456,10460],{"type":23,"tag":151,"props":10439,"children":10440},{"style":764},[10441],{"type":29,"value":10442},"                                     transaction_id",{"type":23,"tag":151,"props":10444,"children":10445},{"style":158},[10446],{"type":29,"value":544},{"type":23,"tag":151,"props":10448,"children":10449},{"style":164},[10450],{"type":29,"value":10451},"transaction.id, ",{"type":23,"tag":151,"props":10453,"children":10454},{"style":764},[10455],{"type":29,"value":9812},{"type":23,"tag":151,"props":10457,"children":10458},{"style":158},[10459],{"type":29,"value":544},{"type":23,"tag":151,"props":10461,"children":10462},{"style":164},[10463],{"type":29,"value":10464},"transaction.payment_instrument_type)\n",{"type":23,"tag":151,"props":10466,"children":10468},{"class":153,"line":10467},74,[10469],{"type":23,"tag":151,"props":10470,"children":10471},{"emptyLinePlaceholder":184},[10472],{"type":29,"value":187},{"type":23,"tag":151,"props":10474,"children":10476},{"class":153,"line":10475},75,[10477,10481,10485,10489,10493,10498],{"type":23,"tag":151,"props":10478,"children":10479},{"style":158},[10480],{"type":29,"value":336},{"type":23,"tag":151,"props":10482,"children":10483},{"style":164},[10484],{"type":29,"value":9685},{"type":23,"tag":151,"props":10486,"children":10487},{"style":764},[10488],{"type":29,"value":7740},{"type":23,"tag":151,"props":10490,"children":10491},{"style":158},[10492],{"type":29,"value":544},{"type":23,"tag":151,"props":10494,"children":10495},{"style":224},[10496],{"type":29,"value":10497},"200",{"type":23,"tag":151,"props":10499,"children":10500},{"style":164},[10501],{"type":29,"value":293},{"type":23,"tag":24,"props":10503,"children":10505},{"id":10504},"some-assembly-required",[10506],{"type":29,"value":10507},"Some Assembly Required",{"type":23,"tag":32,"props":10509,"children":10510},{},[10511],{"type":29,"value":10512},"And there, we're done!",{"type":23,"tag":32,"props":10514,"children":10515},{},[10516,10518,10523],{"type":29,"value":10517},"... for a given value of done. Although we now have something approaching the ",{"type":23,"tag":428,"props":10519,"children":10520},{},[10521],{"type":29,"value":10522},"bare minimum",{"type":29,"value":10524}," for processing a transaction, what we have doesn't account for recurring subscriptions, storing customer information in the braintree 'vault', sub-merchants, handling refunds, performing authorizations and settling at a future time...",{"type":23,"tag":32,"props":10526,"children":10527},{},[10528,10530,10535],{"type":29,"value":10529},"For more information, I direct you once again to braintree's well-written ",{"type":23,"tag":36,"props":10531,"children":10533},{"href":7242,"rel":10532},[40],[10534],{"type":29,"value":7246},{"type":29,"value":10536},", and I would suggest directing any questions you may have to StackOverflow, where I've noted braintree developers to be fairly quick to respond with assistance.",{"type":23,"tag":32,"props":10538,"children":10539},{},[10540],{"type":29,"value":10541},"At this point, however, you should have the beginnings of a working implementation. Soon, your users will be happily trading in their useless monetary units for the happiness and lasting contentment that only cat pictures can provide. Cheers!",{"type":23,"tag":32,"props":10543,"children":10544},{},[10545],{"type":23,"tag":428,"props":10546,"children":10547},{},[10548,10550],{"type":29,"value":10549},"Cat image: CC-BY licensed, via ",{"type":23,"tag":36,"props":10551,"children":10554},{"href":10552,"rel":10553},"https://www.flickr.com/photos/53911972@N03/",[40],[10555],{"type":29,"value":10552},{"type":23,"tag":2836,"props":10557,"children":10558},{},[10559],{"type":29,"value":2840},{"title":8,"searchDepth":190,"depth":190,"links":10561},[10562,10563,10564,10565,10566],{"id":7326,"depth":180,"text":7329},{"id":8027,"depth":180,"text":8030},{"id":8646,"depth":180,"text":8649},{"id":9436,"depth":180,"text":9439},{"id":10504,"depth":180,"text":10507},"content:ckeefer:2016-2:paymentprocessing.md","ckeefer/2016-2/paymentprocessing.md","ckeefer/2016-2/paymentprocessing",{"user":5523,"name":5524},{"_path":10572,"_dir":10573,"_draft":7,"_partial":7,"_locale":8,"title":10574,"description":10575,"publishDate":10576,"tags":10577,"excerpt":10575,"body":10578,"_type":2869,"_id":13455,"_source":2871,"_file":13456,"_stem":13457,"_extension":2874,"author":13458},"/ckeefer/2015-3/emailvalidation","2015-3","Email Validation with Django and python-social-auth","When it comes to user accounts, the standard litmus test is email validation. Besides the immediate benefits - of offering us a straightforward unique identifier for users, and making it more difficult to automate creating a mass of accounts on our service - by requiring that each account have an email address and interact therewith to confirm the addresses validity, it also offers us the chance to associate a known-working email account with a user account. This is important for transactional emails such as password resets or for potential two-factor authentication use... and if you're a little less ethical, for sending marketing desirable and informative emails about interesting products and services.","2015-07-23",[12,14],{"type":20,"children":10579,"toc":13446},[10580,10586,10590,10595,10609,10614,10637,10643,10653,10779,10784,10931,10968,10973,11340,11352,11465,11470,11808,11814,11820,11833,11874,11895,11900,11905,12438,12460,12465,12524,12530,12535,12544,12555,12560,12582,12587,13431,13437,13442],{"type":23,"tag":24,"props":10581,"children":10583},{"id":10582},"so-youre-asking-yourself-robot-or-real-person",[10584],{"type":29,"value":10585},"So you're asking yourself - robot or real person?",{"type":23,"tag":32,"props":10587,"children":10588},{},[10589],{"type":29,"value":10575},{"type":23,"tag":32,"props":10591,"children":10592},{},[10593],{"type":29,"value":10594},"\"But,\" you whine piteously, \"the whole reason I integrated python-social-auth into my project was to let the OAuth providers look after this sort of thing for me!\"",{"type":23,"tag":32,"props":10596,"children":10597},{},[10598,10600,10607],{"type":29,"value":10599},"Tough rocks. ",{"type":23,"tag":36,"props":10601,"children":10604},{"href":10602,"rel":10603},"https://twittercommunity.com/t/how-to-get-email-is-using-twitter-api-on-callback-page-using-php/404/33",[40],[10605],{"type":29,"value":10606},"Twitter ain't gonna give you their users' email addresses",{"type":29,"value":10608},". Just look at all those angry comments. If their whining didn't get through to Twitter, yours isn't going to do the trick either. Besides, eventually you'll probably want to allow the user to login the good old-fashioned way, with a username (which may or may not be their email address) and a password - in which case, you'll want to handle validating their email address yourself anyways.",{"type":23,"tag":32,"props":10610,"children":10611},{},[10612],{"type":29,"value":10613},"So, given that we've already integrated python-social-auth into our Django project (some of the same principles will apply to other frameworks, but many of the details presented herein are specified to Django) - let's get email validation working as part of our user creation/authentication pipeline.",{"type":23,"tag":32,"props":10615,"children":10616},{},[10617,10619,10626,10628,10635],{"type":29,"value":10618},"The ",{"type":23,"tag":36,"props":10620,"children":10623},{"href":10621,"rel":10622},"http://psa.matiasaguirre.net/docs/",[40],[10624],{"type":29,"value":10625},"python-social-auth docs",{"type":29,"value":10627}," have a rather threadbare ",{"type":23,"tag":36,"props":10629,"children":10632},{"href":10630,"rel":10631},"http://psa.matiasaguirre.net/docs/pipeline.html#email-validation",[40],[10633],{"type":29,"value":10634},"section on email validation",{"type":29,"value":10636},", so we won't be reproducing the details discussed there. Instead, let's take a look at some examples of what an implementation should look like, and some workarounds for issues encountered.",{"type":23,"tag":24,"props":10638,"children":10640},{"id":10639},"step-1-acquiring-the-email",[10641],{"type":29,"value":10642},"Step 1 - Acquiring the Email",{"type":23,"tag":32,"props":10644,"children":10645},{},[10646,10648,10652],{"type":29,"value":10647},"To start, we'll need to be able to send emails of our own, so drop the details of your email server/service into ",{"type":23,"tag":2647,"props":10649,"children":10650},{},[10651],{"type":29,"value":3913},{"type":29,"value":4539},{"type":23,"tag":141,"props":10654,"children":10656},{"className":143,"code":10655,"language":12,"meta":8,"style":8},"EMAIL_HOST = 'smtp.yourmailserver.com'\nEMAIL_HOST_USER = 'smtp-username'\nEMAIL_HOST_PASSWORD = 'smtp-password'\n# Port 587 is the default port for smtp submission, but some will use 25, 465, or an arbitrary port #\nEMAIL_PORT = 587  \nEMAIL_USE_TLS = True\nEMAIL_NOREPLY = \"noreply@yourserver.com\"  # Not required, but potentially useful to have defined\n",[10657],{"type":23,"tag":147,"props":10658,"children":10659},{"__ignoreMap":8},[10660,10677,10694,10711,10719,10740,10757],{"type":23,"tag":151,"props":10661,"children":10662},{"class":153,"line":154},[10663,10668,10672],{"type":23,"tag":151,"props":10664,"children":10665},{"style":224},[10666],{"type":29,"value":10667},"EMAIL_HOST",{"type":23,"tag":151,"props":10669,"children":10670},{"style":158},[10671],{"type":29,"value":1753},{"type":23,"tag":151,"props":10673,"children":10674},{"style":285},[10675],{"type":29,"value":10676}," 'smtp.yourmailserver.com'\n",{"type":23,"tag":151,"props":10678,"children":10679},{"class":153,"line":180},[10680,10685,10689],{"type":23,"tag":151,"props":10681,"children":10682},{"style":224},[10683],{"type":29,"value":10684},"EMAIL_HOST_USER",{"type":23,"tag":151,"props":10686,"children":10687},{"style":158},[10688],{"type":29,"value":1753},{"type":23,"tag":151,"props":10690,"children":10691},{"style":285},[10692],{"type":29,"value":10693}," 'smtp-username'\n",{"type":23,"tag":151,"props":10695,"children":10696},{"class":153,"line":190},[10697,10702,10706],{"type":23,"tag":151,"props":10698,"children":10699},{"style":224},[10700],{"type":29,"value":10701},"EMAIL_HOST_PASSWORD",{"type":23,"tag":151,"props":10703,"children":10704},{"style":158},[10705],{"type":29,"value":1753},{"type":23,"tag":151,"props":10707,"children":10708},{"style":285},[10709],{"type":29,"value":10710}," 'smtp-password'\n",{"type":23,"tag":151,"props":10712,"children":10713},{"class":153,"line":220},[10714],{"type":23,"tag":151,"props":10715,"children":10716},{"style":324},[10717],{"type":29,"value":10718},"# Port 587 is the default port for smtp submission, but some will use 25, 465, or an arbitrary port #\n",{"type":23,"tag":151,"props":10720,"children":10721},{"class":153,"line":240},[10722,10727,10731,10736],{"type":23,"tag":151,"props":10723,"children":10724},{"style":224},[10725],{"type":29,"value":10726},"EMAIL_PORT",{"type":23,"tag":151,"props":10728,"children":10729},{"style":158},[10730],{"type":29,"value":1753},{"type":23,"tag":151,"props":10732,"children":10733},{"style":224},[10734],{"type":29,"value":10735}," 587",{"type":23,"tag":151,"props":10737,"children":10738},{"style":164},[10739],{"type":29,"value":1152},{"type":23,"tag":151,"props":10741,"children":10742},{"class":153,"line":254},[10743,10748,10752],{"type":23,"tag":151,"props":10744,"children":10745},{"style":224},[10746],{"type":29,"value":10747},"EMAIL_USE_TLS",{"type":23,"tag":151,"props":10749,"children":10750},{"style":158},[10751],{"type":29,"value":1753},{"type":23,"tag":151,"props":10753,"children":10754},{"style":224},[10755],{"type":29,"value":10756}," True\n",{"type":23,"tag":151,"props":10758,"children":10759},{"class":153,"line":263},[10760,10765,10769,10774],{"type":23,"tag":151,"props":10761,"children":10762},{"style":224},[10763],{"type":29,"value":10764},"EMAIL_NOREPLY",{"type":23,"tag":151,"props":10766,"children":10767},{"style":158},[10768],{"type":29,"value":1753},{"type":23,"tag":151,"props":10770,"children":10771},{"style":285},[10772],{"type":29,"value":10773}," \"noreply@yourserver.com\"",{"type":23,"tag":151,"props":10775,"children":10776},{"style":324},[10777],{"type":29,"value":10778},"  # Not required, but potentially useful to have defined\n",{"type":23,"tag":32,"props":10780,"children":10781},{},[10782],{"type":29,"value":10783},"We'll also need to have setup our social auth pipeline. An example:",{"type":23,"tag":141,"props":10785,"children":10787},{"className":143,"code":10786,"language":12,"meta":8,"style":8},"SOCIAL_AUTH_PIPELINE = (\n    'social.pipeline.social_auth.social_details',\n    'social.pipeline.social_auth.social_uid',\n    'social.pipeline.social_auth.auth_allowed',\n    'social.pipeline.social_auth.social_user',\n    'social.pipeline.user.get_username',\n    'app.auth.user_details.require_email',\n    'social.pipeline.mail.mail_validation',\n    'social.pipeline.social_auth.associate_by_email',\n    'social.pipeline.social_auth.associate_user',\n    'social.pipeline.social_auth.load_extra_data'\n)\n",[10788],{"type":23,"tag":147,"props":10789,"children":10790},{"__ignoreMap":8},[10791,10808,10820,10832,10844,10856,10868,10880,10892,10904,10916,10924],{"type":23,"tag":151,"props":10792,"children":10793},{"class":153,"line":154},[10794,10799,10803],{"type":23,"tag":151,"props":10795,"children":10796},{"style":224},[10797],{"type":29,"value":10798},"SOCIAL_AUTH_PIPELINE",{"type":23,"tag":151,"props":10800,"children":10801},{"style":158},[10802],{"type":29,"value":1753},{"type":23,"tag":151,"props":10804,"children":10805},{"style":164},[10806],{"type":29,"value":10807}," (\n",{"type":23,"tag":151,"props":10809,"children":10810},{"class":153,"line":180},[10811,10816],{"type":23,"tag":151,"props":10812,"children":10813},{"style":285},[10814],{"type":29,"value":10815},"    'social.pipeline.social_auth.social_details'",{"type":23,"tag":151,"props":10817,"children":10818},{"style":164},[10819],{"type":29,"value":1805},{"type":23,"tag":151,"props":10821,"children":10822},{"class":153,"line":190},[10823,10828],{"type":23,"tag":151,"props":10824,"children":10825},{"style":285},[10826],{"type":29,"value":10827},"    'social.pipeline.social_auth.social_uid'",{"type":23,"tag":151,"props":10829,"children":10830},{"style":164},[10831],{"type":29,"value":1805},{"type":23,"tag":151,"props":10833,"children":10834},{"class":153,"line":220},[10835,10840],{"type":23,"tag":151,"props":10836,"children":10837},{"style":285},[10838],{"type":29,"value":10839},"    'social.pipeline.social_auth.auth_allowed'",{"type":23,"tag":151,"props":10841,"children":10842},{"style":164},[10843],{"type":29,"value":1805},{"type":23,"tag":151,"props":10845,"children":10846},{"class":153,"line":240},[10847,10852],{"type":23,"tag":151,"props":10848,"children":10849},{"style":285},[10850],{"type":29,"value":10851},"    'social.pipeline.social_auth.social_user'",{"type":23,"tag":151,"props":10853,"children":10854},{"style":164},[10855],{"type":29,"value":1805},{"type":23,"tag":151,"props":10857,"children":10858},{"class":153,"line":254},[10859,10864],{"type":23,"tag":151,"props":10860,"children":10861},{"style":285},[10862],{"type":29,"value":10863},"    'social.pipeline.user.get_username'",{"type":23,"tag":151,"props":10865,"children":10866},{"style":164},[10867],{"type":29,"value":1805},{"type":23,"tag":151,"props":10869,"children":10870},{"class":153,"line":263},[10871,10876],{"type":23,"tag":151,"props":10872,"children":10873},{"style":285},[10874],{"type":29,"value":10875},"    'app.auth.user_details.require_email'",{"type":23,"tag":151,"props":10877,"children":10878},{"style":164},[10879],{"type":29,"value":1805},{"type":23,"tag":151,"props":10881,"children":10882},{"class":153,"line":272},[10883,10888],{"type":23,"tag":151,"props":10884,"children":10885},{"style":285},[10886],{"type":29,"value":10887},"    'social.pipeline.mail.mail_validation'",{"type":23,"tag":151,"props":10889,"children":10890},{"style":164},[10891],{"type":29,"value":1805},{"type":23,"tag":151,"props":10893,"children":10894},{"class":153,"line":296},[10895,10900],{"type":23,"tag":151,"props":10896,"children":10897},{"style":285},[10898],{"type":29,"value":10899},"    'social.pipeline.social_auth.associate_by_email'",{"type":23,"tag":151,"props":10901,"children":10902},{"style":164},[10903],{"type":29,"value":1805},{"type":23,"tag":151,"props":10905,"children":10906},{"class":153,"line":320},[10907,10912],{"type":23,"tag":151,"props":10908,"children":10909},{"style":285},[10910],{"type":29,"value":10911},"    'social.pipeline.social_auth.associate_user'",{"type":23,"tag":151,"props":10913,"children":10914},{"style":164},[10915],{"type":29,"value":1805},{"type":23,"tag":151,"props":10917,"children":10918},{"class":153,"line":330},[10919],{"type":23,"tag":151,"props":10920,"children":10921},{"style":285},[10922],{"type":29,"value":10923},"    'social.pipeline.social_auth.load_extra_data'\n",{"type":23,"tag":151,"props":10925,"children":10926},{"class":153,"line":901},[10927],{"type":23,"tag":151,"props":10928,"children":10929},{"style":164},[10930],{"type":29,"value":293},{"type":23,"tag":32,"props":10932,"children":10933},{},[10934,10936,10942,10943,10949,10951,10957,10959,10966],{"type":29,"value":10935},"The exact makeup and order of this pipeline will depend on the needs of your project. The elements of interest to us for the sake of setting up email validation are the lines ",{"type":23,"tag":147,"props":10937,"children":10939},{"className":10938},[],[10940],{"type":29,"value":10941},"app.auth.user_details.require_email",{"type":29,"value":68},{"type":23,"tag":147,"props":10944,"children":10946},{"className":10945},[],[10947],{"type":29,"value":10948},"social.pipeline.mail.mail_validation",{"type":29,"value":10950},". Note also that we include these lines before ",{"type":23,"tag":147,"props":10952,"children":10954},{"className":10953},[],[10955],{"type":29,"value":10956},"associate_by_email",{"type":29,"value":10958}," - we don't want to associate users by email until we've confirmed that the user actually owns the email account they've entered, or they could gain control over someone's else's account (see the ",{"type":23,"tag":36,"props":10960,"children":10963},{"href":10961,"rel":10962},"http://psa.matiasaguirre.net/docs/use_cases.html#associate-users-by-email",[40],[10964],{"type":29,"value":10965},"warnings regarding associate_by_email in the python-social-auth docs",{"type":29,"value":10967}," for more details).",{"type":23,"tag":32,"props":10969,"children":10970},{},[10971],{"type":29,"value":10972},"Returning to the two entries in the pipeline I've called out - the former is a partial pipeline function that we create ourselves, to decide whether we need to ask the user for their email at this point in the process. This allows us to proceed seamlessly when we're logging the user in with a service that provides the email (like Google+ or Facebook), while allowing us to present the user with a form to fill in the necessary details otherwise. This fucntion could look something like:",{"type":23,"tag":141,"props":10974,"children":10976},{"className":143,"code":10975,"language":12,"meta":8,"style":8},"from django.shortcuts import redirect\nfrom social.pipeline.partial import partial\nfrom social.pipeline.user import USER_FIELDS\n\ndef require_email(strategy, details, user=None, is_new=False, *args, **kwargs):\n    backend = kwargs.get('backend')\n    if user and user.email:\n        return # The user we're logging in already has their email attribute set\n    elif is_new and and not details.get('email'):\n        # If we're creating a new user, and we can't find the email in the details\n        # we'll attempt to request it from the data returned from our backend strategy\n        userEmail = strategy.request_data().get('email')\n        if userEmail:\n            details['email'] = userEmail\n        else:\n            # If there's no email information to be had, we need to ask the user to fill it in\n            # This should redirect us to a view\n            return redirect('acquire_email')\n",[10977],{"type":23,"tag":147,"props":10978,"children":10979},{"__ignoreMap":8},[10980,11000,11021,11042,11049,11113,11139,11161,11173,11213,11221,11229,11254,11266,11292,11303,11311,11319],{"type":23,"tag":151,"props":10981,"children":10982},{"class":153,"line":154},[10983,10987,10991,10995],{"type":23,"tag":151,"props":10984,"children":10985},{"style":158},[10986],{"type":29,"value":161},{"type":23,"tag":151,"props":10988,"children":10989},{"style":164},[10990],{"type":29,"value":7828},{"type":23,"tag":151,"props":10992,"children":10993},{"style":158},[10994],{"type":29,"value":172},{"type":23,"tag":151,"props":10996,"children":10997},{"style":164},[10998],{"type":29,"value":10999}," redirect\n",{"type":23,"tag":151,"props":11001,"children":11002},{"class":153,"line":180},[11003,11007,11012,11016],{"type":23,"tag":151,"props":11004,"children":11005},{"style":158},[11006],{"type":29,"value":161},{"type":23,"tag":151,"props":11008,"children":11009},{"style":164},[11010],{"type":29,"value":11011}," social.pipeline.partial ",{"type":23,"tag":151,"props":11013,"children":11014},{"style":158},[11015],{"type":29,"value":172},{"type":23,"tag":151,"props":11017,"children":11018},{"style":164},[11019],{"type":29,"value":11020}," partial\n",{"type":23,"tag":151,"props":11022,"children":11023},{"class":153,"line":190},[11024,11028,11033,11037],{"type":23,"tag":151,"props":11025,"children":11026},{"style":158},[11027],{"type":29,"value":161},{"type":23,"tag":151,"props":11029,"children":11030},{"style":164},[11031],{"type":29,"value":11032}," social.pipeline.user ",{"type":23,"tag":151,"props":11034,"children":11035},{"style":158},[11036],{"type":29,"value":172},{"type":23,"tag":151,"props":11038,"children":11039},{"style":224},[11040],{"type":29,"value":11041}," USER_FIELDS\n",{"type":23,"tag":151,"props":11043,"children":11044},{"class":153,"line":220},[11045],{"type":23,"tag":151,"props":11046,"children":11047},{"emptyLinePlaceholder":184},[11048],{"type":29,"value":187},{"type":23,"tag":151,"props":11050,"children":11051},{"class":153,"line":240},[11052,11056,11061,11066,11070,11075,11080,11084,11089,11093,11098,11103,11108],{"type":23,"tag":151,"props":11053,"children":11054},{"style":158},[11055],{"type":29,"value":794},{"type":23,"tag":151,"props":11057,"children":11058},{"style":199},[11059],{"type":29,"value":11060}," require_email",{"type":23,"tag":151,"props":11062,"children":11063},{"style":164},[11064],{"type":29,"value":11065},"(strategy, details, user",{"type":23,"tag":151,"props":11067,"children":11068},{"style":158},[11069],{"type":29,"value":544},{"type":23,"tag":151,"props":11071,"children":11072},{"style":224},[11073],{"type":29,"value":11074},"None",{"type":23,"tag":151,"props":11076,"children":11077},{"style":164},[11078],{"type":29,"value":11079},", is_new",{"type":23,"tag":151,"props":11081,"children":11082},{"style":158},[11083],{"type":29,"value":544},{"type":23,"tag":151,"props":11085,"children":11086},{"style":224},[11087],{"type":29,"value":11088},"False",{"type":23,"tag":151,"props":11090,"children":11091},{"style":164},[11092],{"type":29,"value":370},{"type":23,"tag":151,"props":11094,"children":11095},{"style":158},[11096],{"type":29,"value":11097},"*",{"type":23,"tag":151,"props":11099,"children":11100},{"style":164},[11101],{"type":29,"value":11102},"args, ",{"type":23,"tag":151,"props":11104,"children":11105},{"style":158},[11106],{"type":29,"value":11107},"**",{"type":23,"tag":151,"props":11109,"children":11110},{"style":164},[11111],{"type":29,"value":11112},"kwargs):\n",{"type":23,"tag":151,"props":11114,"children":11115},{"class":153,"line":254},[11116,11121,11125,11130,11135],{"type":23,"tag":151,"props":11117,"children":11118},{"style":164},[11119],{"type":29,"value":11120},"    backend ",{"type":23,"tag":151,"props":11122,"children":11123},{"style":158},[11124],{"type":29,"value":544},{"type":23,"tag":151,"props":11126,"children":11127},{"style":164},[11128],{"type":29,"value":11129}," kwargs.get(",{"type":23,"tag":151,"props":11131,"children":11132},{"style":285},[11133],{"type":29,"value":11134},"'backend'",{"type":23,"tag":151,"props":11136,"children":11137},{"style":164},[11138],{"type":29,"value":293},{"type":23,"tag":151,"props":11140,"children":11141},{"class":153,"line":263},[11142,11146,11151,11156],{"type":23,"tag":151,"props":11143,"children":11144},{"style":158},[11145],{"type":29,"value":10046},{"type":23,"tag":151,"props":11147,"children":11148},{"style":164},[11149],{"type":29,"value":11150}," user ",{"type":23,"tag":151,"props":11152,"children":11153},{"style":158},[11154],{"type":29,"value":11155},"and",{"type":23,"tag":151,"props":11157,"children":11158},{"style":164},[11159],{"type":29,"value":11160}," user.email:\n",{"type":23,"tag":151,"props":11162,"children":11163},{"class":153,"line":272},[11164,11168],{"type":23,"tag":151,"props":11165,"children":11166},{"style":158},[11167],{"type":29,"value":7720},{"type":23,"tag":151,"props":11169,"children":11170},{"style":324},[11171],{"type":29,"value":11172}," # The user we're logging in already has their email attribute set\n",{"type":23,"tag":151,"props":11174,"children":11175},{"class":153,"line":296},[11176,11181,11186,11190,11195,11199,11204,11209],{"type":23,"tag":151,"props":11177,"children":11178},{"style":158},[11179],{"type":29,"value":11180},"    elif",{"type":23,"tag":151,"props":11182,"children":11183},{"style":164},[11184],{"type":29,"value":11185}," is_new ",{"type":23,"tag":151,"props":11187,"children":11188},{"style":158},[11189],{"type":29,"value":11155},{"type":23,"tag":151,"props":11191,"children":11192},{"style":158},[11193],{"type":29,"value":11194}," and",{"type":23,"tag":151,"props":11196,"children":11197},{"style":158},[11198],{"type":29,"value":840},{"type":23,"tag":151,"props":11200,"children":11201},{"style":164},[11202],{"type":29,"value":11203}," details.get(",{"type":23,"tag":151,"props":11205,"children":11206},{"style":285},[11207],{"type":29,"value":11208},"'email'",{"type":23,"tag":151,"props":11210,"children":11211},{"style":164},[11212],{"type":29,"value":217},{"type":23,"tag":151,"props":11214,"children":11215},{"class":153,"line":320},[11216],{"type":23,"tag":151,"props":11217,"children":11218},{"style":324},[11219],{"type":29,"value":11220},"        # If we're creating a new user, and we can't find the email in the details\n",{"type":23,"tag":151,"props":11222,"children":11223},{"class":153,"line":330},[11224],{"type":23,"tag":151,"props":11225,"children":11226},{"style":324},[11227],{"type":29,"value":11228},"        # we'll attempt to request it from the data returned from our backend strategy\n",{"type":23,"tag":151,"props":11230,"children":11231},{"class":153,"line":901},[11232,11237,11241,11246,11250],{"type":23,"tag":151,"props":11233,"children":11234},{"style":164},[11235],{"type":29,"value":11236},"        userEmail ",{"type":23,"tag":151,"props":11238,"children":11239},{"style":158},[11240],{"type":29,"value":544},{"type":23,"tag":151,"props":11242,"children":11243},{"style":164},[11244],{"type":29,"value":11245}," strategy.request_data().get(",{"type":23,"tag":151,"props":11247,"children":11248},{"style":285},[11249],{"type":29,"value":11208},{"type":23,"tag":151,"props":11251,"children":11252},{"style":164},[11253],{"type":29,"value":293},{"type":23,"tag":151,"props":11255,"children":11256},{"class":153,"line":922},[11257,11261],{"type":23,"tag":151,"props":11258,"children":11259},{"style":158},[11260],{"type":29,"value":9360},{"type":23,"tag":151,"props":11262,"children":11263},{"style":164},[11264],{"type":29,"value":11265}," userEmail:\n",{"type":23,"tag":151,"props":11267,"children":11268},{"class":153,"line":930},[11269,11274,11278,11283,11287],{"type":23,"tag":151,"props":11270,"children":11271},{"style":164},[11272],{"type":29,"value":11273},"            details[",{"type":23,"tag":151,"props":11275,"children":11276},{"style":285},[11277],{"type":29,"value":11208},{"type":23,"tag":151,"props":11279,"children":11280},{"style":164},[11281],{"type":29,"value":11282},"] ",{"type":23,"tag":151,"props":11284,"children":11285},{"style":158},[11286],{"type":29,"value":544},{"type":23,"tag":151,"props":11288,"children":11289},{"style":164},[11290],{"type":29,"value":11291}," userEmail\n",{"type":23,"tag":151,"props":11293,"children":11294},{"class":153,"line":971},[11295,11299],{"type":23,"tag":151,"props":11296,"children":11297},{"style":158},[11298],{"type":29,"value":10182},{"type":23,"tag":151,"props":11300,"children":11301},{"style":164},[11302],{"type":29,"value":2226},{"type":23,"tag":151,"props":11304,"children":11305},{"class":153,"line":992},[11306],{"type":23,"tag":151,"props":11307,"children":11308},{"style":324},[11309],{"type":29,"value":11310},"            # If there's no email information to be had, we need to ask the user to fill it in\n",{"type":23,"tag":151,"props":11312,"children":11313},{"class":153,"line":1000},[11314],{"type":23,"tag":151,"props":11315,"children":11316},{"style":324},[11317],{"type":29,"value":11318},"            # This should redirect us to a view\n",{"type":23,"tag":151,"props":11320,"children":11321},{"class":153,"line":1025},[11322,11326,11331,11336],{"type":23,"tag":151,"props":11323,"children":11324},{"style":158},[11325],{"type":29,"value":9396},{"type":23,"tag":151,"props":11327,"children":11328},{"style":164},[11329],{"type":29,"value":11330}," redirect(",{"type":23,"tag":151,"props":11332,"children":11333},{"style":285},[11334],{"type":29,"value":11335},"'acquire_email'",{"type":23,"tag":151,"props":11337,"children":11338},{"style":164},[11339],{"type":29,"value":293},{"type":23,"tag":32,"props":11341,"children":11342},{},[11343,11344,11350],{"type":29,"value":10618},{"type":23,"tag":147,"props":11345,"children":11347},{"className":11346},[],[11348],{"type":29,"value":11349},"acquire_email",{"type":29,"value":11351}," view we're redirecting to can look something like the following:",{"type":23,"tag":141,"props":11353,"children":11355},{"className":143,"code":11354,"language":12,"meta":8,"style":8},"def acquire_email(request, template_name=\"email/acquire.html\"):\n    \"\"\"\n    Request email for the create user flow for logins that don't specify their email address.\n    \"\"\"\n    backend = request.session['partial_pipeline']['backend']\n    return render(request, template_name, {\"backend\": backend})\n",[11356],{"type":23,"tag":147,"props":11357,"children":11358},{"__ignoreMap":8},[11359,11388,11395,11403,11410,11444],{"type":23,"tag":151,"props":11360,"children":11361},{"class":153,"line":154},[11362,11366,11371,11375,11379,11384],{"type":23,"tag":151,"props":11363,"children":11364},{"style":158},[11365],{"type":29,"value":794},{"type":23,"tag":151,"props":11367,"children":11368},{"style":199},[11369],{"type":29,"value":11370}," acquire_email",{"type":23,"tag":151,"props":11372,"children":11373},{"style":164},[11374],{"type":29,"value":7880},{"type":23,"tag":151,"props":11376,"children":11377},{"style":158},[11378],{"type":29,"value":544},{"type":23,"tag":151,"props":11380,"children":11381},{"style":285},[11382],{"type":29,"value":11383},"\"email/acquire.html\"",{"type":23,"tag":151,"props":11385,"children":11386},{"style":164},[11387],{"type":29,"value":217},{"type":23,"tag":151,"props":11389,"children":11390},{"class":153,"line":180},[11391],{"type":23,"tag":151,"props":11392,"children":11393},{"style":285},[11394],{"type":29,"value":7645},{"type":23,"tag":151,"props":11396,"children":11397},{"class":153,"line":190},[11398],{"type":23,"tag":151,"props":11399,"children":11400},{"style":285},[11401],{"type":29,"value":11402},"    Request email for the create user flow for logins that don't specify their email address.\n",{"type":23,"tag":151,"props":11404,"children":11405},{"class":153,"line":220},[11406],{"type":23,"tag":151,"props":11407,"children":11408},{"style":285},[11409],{"type":29,"value":7645},{"type":23,"tag":151,"props":11411,"children":11412},{"class":153,"line":240},[11413,11417,11421,11426,11431,11436,11440],{"type":23,"tag":151,"props":11414,"children":11415},{"style":164},[11416],{"type":29,"value":11120},{"type":23,"tag":151,"props":11418,"children":11419},{"style":158},[11420],{"type":29,"value":544},{"type":23,"tag":151,"props":11422,"children":11423},{"style":164},[11424],{"type":29,"value":11425}," request.session[",{"type":23,"tag":151,"props":11427,"children":11428},{"style":285},[11429],{"type":29,"value":11430},"'partial_pipeline'",{"type":23,"tag":151,"props":11432,"children":11433},{"style":164},[11434],{"type":29,"value":11435},"][",{"type":23,"tag":151,"props":11437,"children":11438},{"style":285},[11439],{"type":29,"value":11134},{"type":23,"tag":151,"props":11441,"children":11442},{"style":164},[11443],{"type":29,"value":4410},{"type":23,"tag":151,"props":11445,"children":11446},{"class":153,"line":254},[11447,11451,11455,11460],{"type":23,"tag":151,"props":11448,"children":11449},{"style":158},[11450],{"type":29,"value":336},{"type":23,"tag":151,"props":11452,"children":11453},{"style":164},[11454],{"type":29,"value":8015},{"type":23,"tag":151,"props":11456,"children":11457},{"style":285},[11458],{"type":29,"value":11459},"\"backend\"",{"type":23,"tag":151,"props":11461,"children":11462},{"style":164},[11463],{"type":29,"value":11464},": backend})\n",{"type":23,"tag":32,"props":11466,"children":11467},{},[11468],{"type":29,"value":11469},"And somewhere within the template, add a form that will submit the required data into the pipeline:",{"type":23,"tag":141,"props":11471,"children":11473},{"className":8451,"code":11472,"language":8453,"meta":8,"style":8},"\u003Cform action=\"{% url \"social:complete\" backend=backend %}\" method=\"post\" role=\"form\">\n    {% csrf_token %}\n    \u003Cdiv class=\"form-group\">\n        \u003Clabel class=\"control-label\" for=\"email\">Email address:\u003C/label>\n        \u003Cinput class=\"form-control\" id=\"email\" type=\"email\" name=\"email\" value=\"\" required />\n    \u003C/div>\n    \u003Cbutton type=\"submit\">Submit\u003C/button>\n\u003C/form>\n",[11474],{"type":23,"tag":147,"props":11475,"children":11476},{"__ignoreMap":8},[11477,11566,11574,11603,11655,11741,11756,11792],{"type":23,"tag":151,"props":11478,"children":11479},{"class":153,"line":154},[11480,11484,11488,11493,11497,11502,11507,11511,11516,11520,11525,11530,11534,11539,11543,11548,11553,11557,11562],{"type":23,"tag":151,"props":11481,"children":11482},{"style":164},[11483],{"type":29,"value":10301},{"type":23,"tag":151,"props":11485,"children":11486},{"style":8468},[11487],{"type":29,"value":8496},{"type":23,"tag":151,"props":11489,"children":11490},{"style":199},[11491],{"type":29,"value":11492}," action",{"type":23,"tag":151,"props":11494,"children":11495},{"style":164},[11496],{"type":29,"value":544},{"type":23,"tag":151,"props":11498,"children":11499},{"style":285},[11500],{"type":29,"value":11501},"\"{% url \"",{"type":23,"tag":151,"props":11503,"children":11504},{"style":199},[11505],{"type":29,"value":11506},"social:complete",{"type":23,"tag":151,"props":11508,"children":11509},{"style":4953},[11510],{"type":29,"value":497},{"type":23,"tag":151,"props":11512,"children":11513},{"style":199},[11514],{"type":29,"value":11515}," backend",{"type":23,"tag":151,"props":11517,"children":11518},{"style":164},[11519],{"type":29,"value":544},{"type":23,"tag":151,"props":11521,"children":11522},{"style":285},[11523],{"type":29,"value":11524},"backend",{"type":23,"tag":151,"props":11526,"children":11527},{"style":199},[11528],{"type":29,"value":11529}," %}",{"type":23,"tag":151,"props":11531,"children":11532},{"style":4953},[11533],{"type":29,"value":497},{"type":23,"tag":151,"props":11535,"children":11536},{"style":199},[11537],{"type":29,"value":11538}," method",{"type":23,"tag":151,"props":11540,"children":11541},{"style":164},[11542],{"type":29,"value":544},{"type":23,"tag":151,"props":11544,"children":11545},{"style":285},[11546],{"type":29,"value":11547},"\"post\"",{"type":23,"tag":151,"props":11549,"children":11550},{"style":199},[11551],{"type":29,"value":11552}," role",{"type":23,"tag":151,"props":11554,"children":11555},{"style":164},[11556],{"type":29,"value":544},{"type":23,"tag":151,"props":11558,"children":11559},{"style":285},[11560],{"type":29,"value":11561},"\"form\"",{"type":23,"tag":151,"props":11563,"children":11564},{"style":164},[11565],{"type":29,"value":8476},{"type":23,"tag":151,"props":11567,"children":11568},{"class":153,"line":180},[11569],{"type":23,"tag":151,"props":11570,"children":11571},{"style":164},[11572],{"type":29,"value":11573},"    {% csrf_token %}\n",{"type":23,"tag":151,"props":11575,"children":11576},{"class":153,"line":190},[11577,11581,11585,11590,11594,11599],{"type":23,"tag":151,"props":11578,"children":11579},{"style":164},[11580],{"type":29,"value":8465},{"type":23,"tag":151,"props":11582,"children":11583},{"style":8468},[11584],{"type":29,"value":8513},{"type":23,"tag":151,"props":11586,"children":11587},{"style":199},[11588],{"type":29,"value":11589}," class",{"type":23,"tag":151,"props":11591,"children":11592},{"style":164},[11593],{"type":29,"value":544},{"type":23,"tag":151,"props":11595,"children":11596},{"style":285},[11597],{"type":29,"value":11598},"\"form-group\"",{"type":23,"tag":151,"props":11600,"children":11601},{"style":164},[11602],{"type":29,"value":8476},{"type":23,"tag":151,"props":11604,"children":11605},{"class":153,"line":220},[11606,11610,11615,11619,11623,11628,11633,11637,11642,11647,11651],{"type":23,"tag":151,"props":11607,"children":11608},{"style":164},[11609],{"type":29,"value":8491},{"type":23,"tag":151,"props":11611,"children":11612},{"style":8468},[11613],{"type":29,"value":11614},"label",{"type":23,"tag":151,"props":11616,"children":11617},{"style":199},[11618],{"type":29,"value":11589},{"type":23,"tag":151,"props":11620,"children":11621},{"style":164},[11622],{"type":29,"value":544},{"type":23,"tag":151,"props":11624,"children":11625},{"style":285},[11626],{"type":29,"value":11627},"\"control-label\"",{"type":23,"tag":151,"props":11629,"children":11630},{"style":199},[11631],{"type":29,"value":11632}," for",{"type":23,"tag":151,"props":11634,"children":11635},{"style":164},[11636],{"type":29,"value":544},{"type":23,"tag":151,"props":11638,"children":11639},{"style":285},[11640],{"type":29,"value":11641},"\"email\"",{"type":23,"tag":151,"props":11643,"children":11644},{"style":164},[11645],{"type":29,"value":11646},">Email address:\u003C/",{"type":23,"tag":151,"props":11648,"children":11649},{"style":8468},[11650],{"type":29,"value":11614},{"type":23,"tag":151,"props":11652,"children":11653},{"style":164},[11654],{"type":29,"value":8476},{"type":23,"tag":151,"props":11656,"children":11657},{"class":153,"line":240},[11658,11662,11667,11671,11675,11680,11684,11688,11692,11696,11700,11704,11709,11713,11717,11722,11726,11731,11736],{"type":23,"tag":151,"props":11659,"children":11660},{"style":164},[11661],{"type":29,"value":8491},{"type":23,"tag":151,"props":11663,"children":11664},{"style":8468},[11665],{"type":29,"value":11666},"input",{"type":23,"tag":151,"props":11668,"children":11669},{"style":199},[11670],{"type":29,"value":11589},{"type":23,"tag":151,"props":11672,"children":11673},{"style":164},[11674],{"type":29,"value":544},{"type":23,"tag":151,"props":11676,"children":11677},{"style":285},[11678],{"type":29,"value":11679},"\"form-control\"",{"type":23,"tag":151,"props":11681,"children":11682},{"style":199},[11683],{"type":29,"value":8518},{"type":23,"tag":151,"props":11685,"children":11686},{"style":164},[11687],{"type":29,"value":544},{"type":23,"tag":151,"props":11689,"children":11690},{"style":285},[11691],{"type":29,"value":11641},{"type":23,"tag":151,"props":11693,"children":11694},{"style":199},[11695],{"type":29,"value":8564},{"type":23,"tag":151,"props":11697,"children":11698},{"style":164},[11699],{"type":29,"value":544},{"type":23,"tag":151,"props":11701,"children":11702},{"style":285},[11703],{"type":29,"value":11641},{"type":23,"tag":151,"props":11705,"children":11706},{"style":199},[11707],{"type":29,"value":11708}," name",{"type":23,"tag":151,"props":11710,"children":11711},{"style":164},[11712],{"type":29,"value":544},{"type":23,"tag":151,"props":11714,"children":11715},{"style":285},[11716],{"type":29,"value":11641},{"type":23,"tag":151,"props":11718,"children":11719},{"style":199},[11720],{"type":29,"value":11721}," value",{"type":23,"tag":151,"props":11723,"children":11724},{"style":164},[11725],{"type":29,"value":544},{"type":23,"tag":151,"props":11727,"children":11728},{"style":285},[11729],{"type":29,"value":11730},"\"\"",{"type":23,"tag":151,"props":11732,"children":11733},{"style":199},[11734],{"type":29,"value":11735}," required",{"type":23,"tag":151,"props":11737,"children":11738},{"style":164},[11739],{"type":29,"value":11740}," />\n",{"type":23,"tag":151,"props":11742,"children":11743},{"class":153,"line":254},[11744,11748,11752],{"type":23,"tag":151,"props":11745,"children":11746},{"style":164},[11747],{"type":29,"value":8617},{"type":23,"tag":151,"props":11749,"children":11750},{"style":8468},[11751],{"type":29,"value":8513},{"type":23,"tag":151,"props":11753,"children":11754},{"style":164},[11755],{"type":29,"value":8476},{"type":23,"tag":151,"props":11757,"children":11758},{"class":153,"line":263},[11759,11763,11767,11771,11775,11779,11784,11788],{"type":23,"tag":151,"props":11760,"children":11761},{"style":164},[11762],{"type":29,"value":8465},{"type":23,"tag":151,"props":11764,"children":11765},{"style":8468},[11766],{"type":29,"value":8559},{"type":23,"tag":151,"props":11768,"children":11769},{"style":199},[11770],{"type":29,"value":8564},{"type":23,"tag":151,"props":11772,"children":11773},{"style":164},[11774],{"type":29,"value":544},{"type":23,"tag":151,"props":11776,"children":11777},{"style":285},[11778],{"type":29,"value":8573},{"type":23,"tag":151,"props":11780,"children":11781},{"style":164},[11782],{"type":29,"value":11783},">Submit\u003C/",{"type":23,"tag":151,"props":11785,"children":11786},{"style":8468},[11787],{"type":29,"value":8559},{"type":23,"tag":151,"props":11789,"children":11790},{"style":164},[11791],{"type":29,"value":8476},{"type":23,"tag":151,"props":11793,"children":11794},{"class":153,"line":272},[11795,11800,11804],{"type":23,"tag":151,"props":11796,"children":11797},{"style":164},[11798],{"type":29,"value":11799},"\u003C/",{"type":23,"tag":151,"props":11801,"children":11802},{"style":8468},[11803],{"type":29,"value":8496},{"type":23,"tag":151,"props":11805,"children":11806},{"style":164},[11807],{"type":29,"value":8476},{"type":23,"tag":5675,"props":11809,"children":11811},{"id":11810},"thats-step-1-complete",[11812],{"type":29,"value":11813},"That's step 1 complete...",{"type":23,"tag":24,"props":11815,"children":11817},{"id":11816},"step-2-validating-the-email",[11818],{"type":29,"value":11819},"Step 2 - Validating the email",{"type":23,"tag":32,"props":11821,"children":11822},{},[11823,11825,11831],{"type":29,"value":11824},"The latter of the two pipeline functions called out is the ",{"type":23,"tag":36,"props":11826,"children":11828},{"href":10630,"rel":11827},[40],[11829],{"type":29,"value":11830},"email validation partial pipeline discussed in the docs",{"type":29,"value":11832},". In order to effectively use it, we need to include a few additional entries in settings.py, as well as create some further custom functions.",{"type":23,"tag":141,"props":11834,"children":11836},{"className":143,"code":11835,"language":12,"meta":8,"style":8},"SOCIAL_AUTH_EMAIL_VALIDATION_FUNCTION = 'app.email.SendVerificationEmail'\nSOCIAL_AUTH_EMAIL_VALIDATION_URL = '/email_verify_sent/'\n",[11837],{"type":23,"tag":147,"props":11838,"children":11839},{"__ignoreMap":8},[11840,11857],{"type":23,"tag":151,"props":11841,"children":11842},{"class":153,"line":154},[11843,11848,11852],{"type":23,"tag":151,"props":11844,"children":11845},{"style":224},[11846],{"type":29,"value":11847},"SOCIAL_AUTH_EMAIL_VALIDATION_FUNCTION",{"type":23,"tag":151,"props":11849,"children":11850},{"style":158},[11851],{"type":29,"value":1753},{"type":23,"tag":151,"props":11853,"children":11854},{"style":285},[11855],{"type":29,"value":11856}," 'app.email.SendVerificationEmail'\n",{"type":23,"tag":151,"props":11858,"children":11859},{"class":153,"line":180},[11860,11865,11869],{"type":23,"tag":151,"props":11861,"children":11862},{"style":224},[11863],{"type":29,"value":11864},"SOCIAL_AUTH_EMAIL_VALIDATION_URL",{"type":23,"tag":151,"props":11866,"children":11867},{"style":158},[11868],{"type":29,"value":1753},{"type":23,"tag":151,"props":11870,"children":11871},{"style":285},[11872],{"type":29,"value":11873}," '/email_verify_sent/'\n",{"type":23,"tag":32,"props":11875,"children":11876},{},[11877,11879,11885,11887,11893],{"type":29,"value":11878},"The latter of these two is the url that the user will be redirected to once the validation email has been sent - you should add a view and url entry for this as per usual. For the former, we'll need to create a function that actually sends the verifiction email. Here we've created it in a module ",{"type":23,"tag":147,"props":11880,"children":11882},{"className":11881},[],[11883],{"type":29,"value":11884},"email",{"type":29,"value":11886}," within the application ",{"type":23,"tag":147,"props":11888,"children":11890},{"className":11889},[],[11891],{"type":29,"value":11892},"app",{"type":29,"value":11894},", but those are details that can be altered according to your project's needs.",{"type":23,"tag":32,"props":11896,"children":11897},{},[11898],{"type":29,"value":11899},"The function itself needs to build the email text you're going to send, and include some details that will enable the email validation partial_pipeline to resume. We'll be including more than the standard recommended details for reasons we'll discuss momentarily.",{"type":23,"tag":32,"props":11901,"children":11902},{},[11903],{"type":29,"value":11904},"Your email verification function should look something like the following:",{"type":23,"tag":141,"props":11906,"children":11908},{"className":143,"code":11907,"language":12,"meta":8,"style":8},"from django.core import signing\nfrom django.core.mail import EmailMultiAlternatives\n\ndef SendVerificationEmail(strategy, backend, code):\n    \"\"\"\n    Send an email with an embedded verification code and the necessary details to restore the required session\n    elements to complete the verification and sign-in, regardless of what browser the user completes the\n    verification from.\n    \"\"\"\n    signature = signing.dumps({\"session_key\": strategy.session.session_key, \"email\": code.email},\n                              key=settings.EMAIL_SECRET_KEY)\n    verifyURL = \"{0}?verification_code={1}&signature={2}\".format(\n        reverse('social:complete', args=(backend.name,)),\n        code.code, signature)\n    verifyURL = strategy.request.build_absolute_uri(verifyURL)   \n\n    emailHTML = # Include your function that returns an html string here\n    emailText = \"\"\"Welcome to MyApp!\nIn order to login with your new user account, you need to verify your email address with us.\nPlease copy and paste the following into your browser's url bar: {verifyURL}\n\"\"\".format(verifyURL=verifyURL)\n\n    kwargs = {\n        \"subject\": \"Verify Your Account\",\n        \"body\": emailText,\n        \"from_email\": \"MyApp \",\n        \"to\": [\"recipient@email.address\"],\n    }\n\n    email = EmailMultiAlternatives(**kwargs)\n    email.attach_alternative(emailHTML, \"text/html\")\n    email.send()\n",[11909],{"type":23,"tag":147,"props":11910,"children":11911},{"__ignoreMap":8},[11912,11933,11954,11961,11978,11985,11993,12001,12009,12016,12052,12078,12129,12160,12168,12184,12191,12208,12225,12233,12246,12273,12280,12296,12317,12330,12351,12373,12380,12387,12413,12430],{"type":23,"tag":151,"props":11913,"children":11914},{"class":153,"line":154},[11915,11919,11924,11928],{"type":23,"tag":151,"props":11916,"children":11917},{"style":158},[11918],{"type":29,"value":161},{"type":23,"tag":151,"props":11920,"children":11921},{"style":164},[11922],{"type":29,"value":11923}," django.core ",{"type":23,"tag":151,"props":11925,"children":11926},{"style":158},[11927],{"type":29,"value":172},{"type":23,"tag":151,"props":11929,"children":11930},{"style":164},[11931],{"type":29,"value":11932}," signing\n",{"type":23,"tag":151,"props":11934,"children":11935},{"class":153,"line":180},[11936,11940,11945,11949],{"type":23,"tag":151,"props":11937,"children":11938},{"style":158},[11939],{"type":29,"value":161},{"type":23,"tag":151,"props":11941,"children":11942},{"style":164},[11943],{"type":29,"value":11944}," django.core.mail ",{"type":23,"tag":151,"props":11946,"children":11947},{"style":158},[11948],{"type":29,"value":172},{"type":23,"tag":151,"props":11950,"children":11951},{"style":164},[11952],{"type":29,"value":11953}," EmailMultiAlternatives\n",{"type":23,"tag":151,"props":11955,"children":11956},{"class":153,"line":190},[11957],{"type":23,"tag":151,"props":11958,"children":11959},{"emptyLinePlaceholder":184},[11960],{"type":29,"value":187},{"type":23,"tag":151,"props":11962,"children":11963},{"class":153,"line":220},[11964,11968,11973],{"type":23,"tag":151,"props":11965,"children":11966},{"style":158},[11967],{"type":29,"value":794},{"type":23,"tag":151,"props":11969,"children":11970},{"style":199},[11971],{"type":29,"value":11972}," SendVerificationEmail",{"type":23,"tag":151,"props":11974,"children":11975},{"style":164},[11976],{"type":29,"value":11977},"(strategy, backend, code):\n",{"type":23,"tag":151,"props":11979,"children":11980},{"class":153,"line":240},[11981],{"type":23,"tag":151,"props":11982,"children":11983},{"style":285},[11984],{"type":29,"value":7645},{"type":23,"tag":151,"props":11986,"children":11987},{"class":153,"line":254},[11988],{"type":23,"tag":151,"props":11989,"children":11990},{"style":285},[11991],{"type":29,"value":11992},"    Send an email with an embedded verification code and the necessary details to restore the required session\n",{"type":23,"tag":151,"props":11994,"children":11995},{"class":153,"line":263},[11996],{"type":23,"tag":151,"props":11997,"children":11998},{"style":285},[11999],{"type":29,"value":12000},"    elements to complete the verification and sign-in, regardless of what browser the user completes the\n",{"type":23,"tag":151,"props":12002,"children":12003},{"class":153,"line":272},[12004],{"type":23,"tag":151,"props":12005,"children":12006},{"style":285},[12007],{"type":29,"value":12008},"    verification from.\n",{"type":23,"tag":151,"props":12010,"children":12011},{"class":153,"line":296},[12012],{"type":23,"tag":151,"props":12013,"children":12014},{"style":285},[12015],{"type":29,"value":7645},{"type":23,"tag":151,"props":12017,"children":12018},{"class":153,"line":320},[12019,12024,12028,12033,12038,12043,12047],{"type":23,"tag":151,"props":12020,"children":12021},{"style":164},[12022],{"type":29,"value":12023},"    signature ",{"type":23,"tag":151,"props":12025,"children":12026},{"style":158},[12027],{"type":29,"value":544},{"type":23,"tag":151,"props":12029,"children":12030},{"style":164},[12031],{"type":29,"value":12032}," signing.dumps({",{"type":23,"tag":151,"props":12034,"children":12035},{"style":285},[12036],{"type":29,"value":12037},"\"session_key\"",{"type":23,"tag":151,"props":12039,"children":12040},{"style":164},[12041],{"type":29,"value":12042},": strategy.session.session_key, ",{"type":23,"tag":151,"props":12044,"children":12045},{"style":285},[12046],{"type":29,"value":11641},{"type":23,"tag":151,"props":12048,"children":12049},{"style":164},[12050],{"type":29,"value":12051},": code.email},\n",{"type":23,"tag":151,"props":12053,"children":12054},{"class":153,"line":330},[12055,12060,12064,12069,12074],{"type":23,"tag":151,"props":12056,"children":12057},{"style":764},[12058],{"type":29,"value":12059},"                              key",{"type":23,"tag":151,"props":12061,"children":12062},{"style":158},[12063],{"type":29,"value":544},{"type":23,"tag":151,"props":12065,"children":12066},{"style":164},[12067],{"type":29,"value":12068},"settings.",{"type":23,"tag":151,"props":12070,"children":12071},{"style":224},[12072],{"type":29,"value":12073},"EMAIL_SECRET_KEY",{"type":23,"tag":151,"props":12075,"children":12076},{"style":164},[12077],{"type":29,"value":293},{"type":23,"tag":151,"props":12079,"children":12080},{"class":153,"line":901},[12081,12086,12090,12095,12100,12105,12110,12115,12120,12124],{"type":23,"tag":151,"props":12082,"children":12083},{"style":164},[12084],{"type":29,"value":12085},"    verifyURL ",{"type":23,"tag":151,"props":12087,"children":12088},{"style":158},[12089],{"type":29,"value":544},{"type":23,"tag":151,"props":12091,"children":12092},{"style":285},[12093],{"type":29,"value":12094}," \"",{"type":23,"tag":151,"props":12096,"children":12097},{"style":224},[12098],{"type":29,"value":12099},"{0}",{"type":23,"tag":151,"props":12101,"children":12102},{"style":285},[12103],{"type":29,"value":12104},"?verification_code=",{"type":23,"tag":151,"props":12106,"children":12107},{"style":224},[12108],{"type":29,"value":12109},"{1}",{"type":23,"tag":151,"props":12111,"children":12112},{"style":285},[12113],{"type":29,"value":12114},"&signature=",{"type":23,"tag":151,"props":12116,"children":12117},{"style":224},[12118],{"type":29,"value":12119},"{2}",{"type":23,"tag":151,"props":12121,"children":12122},{"style":285},[12123],{"type":29,"value":497},{"type":23,"tag":151,"props":12125,"children":12126},{"style":164},[12127],{"type":29,"value":12128},".format(\n",{"type":23,"tag":151,"props":12130,"children":12131},{"class":153,"line":922},[12132,12137,12142,12146,12151,12155],{"type":23,"tag":151,"props":12133,"children":12134},{"style":164},[12135],{"type":29,"value":12136},"        reverse(",{"type":23,"tag":151,"props":12138,"children":12139},{"style":285},[12140],{"type":29,"value":12141},"'social:complete'",{"type":23,"tag":151,"props":12143,"children":12144},{"style":164},[12145],{"type":29,"value":370},{"type":23,"tag":151,"props":12147,"children":12148},{"style":764},[12149],{"type":29,"value":12150},"args",{"type":23,"tag":151,"props":12152,"children":12153},{"style":158},[12154],{"type":29,"value":544},{"type":23,"tag":151,"props":12156,"children":12157},{"style":164},[12158],{"type":29,"value":12159},"(backend.name,)),\n",{"type":23,"tag":151,"props":12161,"children":12162},{"class":153,"line":930},[12163],{"type":23,"tag":151,"props":12164,"children":12165},{"style":164},[12166],{"type":29,"value":12167},"        code.code, signature)\n",{"type":23,"tag":151,"props":12169,"children":12170},{"class":153,"line":971},[12171,12175,12179],{"type":23,"tag":151,"props":12172,"children":12173},{"style":164},[12174],{"type":29,"value":12085},{"type":23,"tag":151,"props":12176,"children":12177},{"style":158},[12178],{"type":29,"value":544},{"type":23,"tag":151,"props":12180,"children":12181},{"style":164},[12182],{"type":29,"value":12183}," strategy.request.build_absolute_uri(verifyURL)   \n",{"type":23,"tag":151,"props":12185,"children":12186},{"class":153,"line":992},[12187],{"type":23,"tag":151,"props":12188,"children":12189},{"emptyLinePlaceholder":184},[12190],{"type":29,"value":187},{"type":23,"tag":151,"props":12192,"children":12193},{"class":153,"line":1000},[12194,12199,12203],{"type":23,"tag":151,"props":12195,"children":12196},{"style":164},[12197],{"type":29,"value":12198},"    emailHTML ",{"type":23,"tag":151,"props":12200,"children":12201},{"style":158},[12202],{"type":29,"value":544},{"type":23,"tag":151,"props":12204,"children":12205},{"style":324},[12206],{"type":29,"value":12207}," # Include your function that returns an html string here\n",{"type":23,"tag":151,"props":12209,"children":12210},{"class":153,"line":1025},[12211,12216,12220],{"type":23,"tag":151,"props":12212,"children":12213},{"style":164},[12214],{"type":29,"value":12215},"    emailText ",{"type":23,"tag":151,"props":12217,"children":12218},{"style":158},[12219],{"type":29,"value":544},{"type":23,"tag":151,"props":12221,"children":12222},{"style":285},[12223],{"type":29,"value":12224}," \"\"\"Welcome to MyApp!\n",{"type":23,"tag":151,"props":12226,"children":12227},{"class":153,"line":1046},[12228],{"type":23,"tag":151,"props":12229,"children":12230},{"style":285},[12231],{"type":29,"value":12232},"In order to login with your new user account, you need to verify your email address with us.\n",{"type":23,"tag":151,"props":12234,"children":12235},{"class":153,"line":1054},[12236,12241],{"type":23,"tag":151,"props":12237,"children":12238},{"style":285},[12239],{"type":29,"value":12240},"Please copy and paste the following into your browser's url bar: ",{"type":23,"tag":151,"props":12242,"children":12243},{"style":224},[12244],{"type":29,"value":12245},"{verifyURL}\n",{"type":23,"tag":151,"props":12247,"children":12248},{"class":153,"line":1091},[12249,12254,12259,12264,12268],{"type":23,"tag":151,"props":12250,"children":12251},{"style":285},[12252],{"type":29,"value":12253},"\"\"\"",{"type":23,"tag":151,"props":12255,"children":12256},{"style":164},[12257],{"type":29,"value":12258},".format(",{"type":23,"tag":151,"props":12260,"children":12261},{"style":764},[12262],{"type":29,"value":12263},"verifyURL",{"type":23,"tag":151,"props":12265,"children":12266},{"style":158},[12267],{"type":29,"value":544},{"type":23,"tag":151,"props":12269,"children":12270},{"style":164},[12271],{"type":29,"value":12272},"verifyURL)\n",{"type":23,"tag":151,"props":12274,"children":12275},{"class":153,"line":1112},[12276],{"type":23,"tag":151,"props":12277,"children":12278},{"emptyLinePlaceholder":184},[12279],{"type":29,"value":187},{"type":23,"tag":151,"props":12281,"children":12282},{"class":153,"line":1120},[12283,12288,12292],{"type":23,"tag":151,"props":12284,"children":12285},{"style":164},[12286],{"type":29,"value":12287},"    kwargs ",{"type":23,"tag":151,"props":12289,"children":12290},{"style":158},[12291],{"type":29,"value":544},{"type":23,"tag":151,"props":12293,"children":12294},{"style":164},[12295],{"type":29,"value":3946},{"type":23,"tag":151,"props":12297,"children":12298},{"class":153,"line":1129},[12299,12304,12308,12313],{"type":23,"tag":151,"props":12300,"children":12301},{"style":285},[12302],{"type":29,"value":12303},"        \"subject\"",{"type":23,"tag":151,"props":12305,"children":12306},{"style":164},[12307],{"type":29,"value":232},{"type":23,"tag":151,"props":12309,"children":12310},{"style":285},[12311],{"type":29,"value":12312},"\"Verify Your Account\"",{"type":23,"tag":151,"props":12314,"children":12315},{"style":164},[12316],{"type":29,"value":1805},{"type":23,"tag":151,"props":12318,"children":12319},{"class":153,"line":1137},[12320,12325],{"type":23,"tag":151,"props":12321,"children":12322},{"style":285},[12323],{"type":29,"value":12324},"        \"body\"",{"type":23,"tag":151,"props":12326,"children":12327},{"style":164},[12328],{"type":29,"value":12329},": emailText,\n",{"type":23,"tag":151,"props":12331,"children":12332},{"class":153,"line":1146},[12333,12338,12342,12347],{"type":23,"tag":151,"props":12334,"children":12335},{"style":285},[12336],{"type":29,"value":12337},"        \"from_email\"",{"type":23,"tag":151,"props":12339,"children":12340},{"style":164},[12341],{"type":29,"value":232},{"type":23,"tag":151,"props":12343,"children":12344},{"style":285},[12345],{"type":29,"value":12346},"\"MyApp \"",{"type":23,"tag":151,"props":12348,"children":12349},{"style":164},[12350],{"type":29,"value":1805},{"type":23,"tag":151,"props":12352,"children":12353},{"class":153,"line":1155},[12354,12359,12364,12369],{"type":23,"tag":151,"props":12355,"children":12356},{"style":285},[12357],{"type":29,"value":12358},"        \"to\"",{"type":23,"tag":151,"props":12360,"children":12361},{"style":164},[12362],{"type":29,"value":12363},": [",{"type":23,"tag":151,"props":12365,"children":12366},{"style":285},[12367],{"type":29,"value":12368},"\"recipient@email.address\"",{"type":23,"tag":151,"props":12370,"children":12371},{"style":164},[12372],{"type":29,"value":8302},{"type":23,"tag":151,"props":12374,"children":12375},{"class":153,"line":2034},[12376],{"type":23,"tag":151,"props":12377,"children":12378},{"style":164},[12379],{"type":29,"value":1859},{"type":23,"tag":151,"props":12381,"children":12382},{"class":153,"line":2043},[12383],{"type":23,"tag":151,"props":12384,"children":12385},{"emptyLinePlaceholder":184},[12386],{"type":29,"value":187},{"type":23,"tag":151,"props":12388,"children":12389},{"class":153,"line":2052},[12390,12395,12399,12404,12408],{"type":23,"tag":151,"props":12391,"children":12392},{"style":164},[12393],{"type":29,"value":12394},"    email ",{"type":23,"tag":151,"props":12396,"children":12397},{"style":158},[12398],{"type":29,"value":544},{"type":23,"tag":151,"props":12400,"children":12401},{"style":164},[12402],{"type":29,"value":12403}," EmailMultiAlternatives(",{"type":23,"tag":151,"props":12405,"children":12406},{"style":158},[12407],{"type":29,"value":11107},{"type":23,"tag":151,"props":12409,"children":12410},{"style":164},[12411],{"type":29,"value":12412},"kwargs)\n",{"type":23,"tag":151,"props":12414,"children":12415},{"class":153,"line":2061},[12416,12421,12426],{"type":23,"tag":151,"props":12417,"children":12418},{"style":164},[12419],{"type":29,"value":12420},"    email.attach_alternative(emailHTML, ",{"type":23,"tag":151,"props":12422,"children":12423},{"style":285},[12424],{"type":29,"value":12425},"\"text/html\"",{"type":23,"tag":151,"props":12427,"children":12428},{"style":164},[12429],{"type":29,"value":293},{"type":23,"tag":151,"props":12431,"children":12432},{"class":153,"line":2070},[12433],{"type":23,"tag":151,"props":12434,"children":12435},{"style":164},[12436],{"type":29,"value":12437},"    email.send()\n",{"type":23,"tag":32,"props":12439,"children":12440},{},[12441,12443,12450,12452,12459],{"type":29,"value":12442},"You'll notice we used Django's ",{"type":23,"tag":36,"props":12444,"children":12447},{"href":12445,"rel":12446},"https://docs.djangoproject.com/en/1.8/topics/email/#django.core.mail.EmailMessage",[40],[12448],{"type":29,"value":12449},"EmailMultiAlternatives",{"type":29,"value":12451}," to attach an html version of the email. You can find more details on sending email with Django in the ",{"type":23,"tag":36,"props":12453,"children":12456},{"href":12454,"rel":12455},"https://docs.djangoproject.com/en/1.8/topics/email/",[40],[12457],{"type":29,"value":12458},"Django docs",{"type":29,"value":386},{"type":23,"tag":32,"props":12461,"children":12462},{},[12463],{"type":29,"value":12464},"You'll also likely have noticed that we're including some signed details in that verification email string we're sending. What details are for what purpose? Let's break it down, and discuss some necessary evil.",{"type":23,"tag":2639,"props":12466,"children":12467},{},[12468,12479,12490],{"type":23,"tag":2643,"props":12469,"children":12470},{},[12471,12477],{"type":23,"tag":147,"props":12472,"children":12474},{"className":12473},[],[12475],{"type":29,"value":12476},"code.code",{"type":29,"value":12478}," is the email verification code generated by the mail validation pipeline.",{"type":23,"tag":2643,"props":12480,"children":12481},{},[12482,12488],{"type":23,"tag":147,"props":12483,"children":12485},{"className":12484},[],[12486],{"type":29,"value":12487},"code.email",{"type":29,"value":12489}," is the email address we're attempting to verify.",{"type":23,"tag":2643,"props":12491,"children":12492},{},[12493,12499,12501,12507,12509,12515,12517,12523],{"type":23,"tag":147,"props":12494,"children":12496},{"className":12495},[],[12497],{"type":29,"value":12498},"signature",{"type":29,"value":12500}," is our own addition to the process - you'll notice it include both the email we're trying to verify, and a ",{"type":23,"tag":147,"props":12502,"children":12504},{"className":12503},[],[12505],{"type":29,"value":12506},"session_key",{"type":29,"value":12508},", which contains the session_key stored in our current backend strategy. Finally, ",{"type":23,"tag":147,"props":12510,"children":12512},{"className":12511},[],[12513],{"type":29,"value":12514},"key",{"type":29,"value":12516}," is the secret we're using to sign this signature with, to prevent tampering - you can find more details on signing in the ",{"type":23,"tag":36,"props":12518,"children":12521},{"href":12519,"rel":12520},"https://docs.djangoproject.com/en/1.8/topics/signing/",[40],[12522],{"type":29,"value":12458},{"type":29,"value":386},{"type":23,"tag":24,"props":12525,"children":12527},{"id":12526},"step-3-validating-the-email-from-any-browser",[12528],{"type":29,"value":12529},"Step 3 - Validating the email from any browser",{"type":23,"tag":32,"props":12531,"children":12532},{},[12533],{"type":29,"value":12534},"What do we need that signature for, though? This may have been remedied in whatever version of python-social-auth your using, but at the time of this writing (July 2015), if your receiving user were to click/paste the link you included in your validation email into a different browser, they'd likely be confronted with a 500 error page, and you'll have an entry in your log:",{"type":23,"tag":141,"props":12536,"children":12539},{"className":12537,"code":12538,"language":29},[586],"Partial pipeline can not resume\n",[12540],{"type":23,"tag":147,"props":12541,"children":12542},{"__ignoreMap":8},[12543],{"type":29,"value":12538},{"type":23,"tag":32,"props":12545,"children":12546},{},[12547,12549,12554],{"type":29,"value":12548},"In fact, if they were to click that link in the same browser, but after clearing their cache, or after a long period of time causes their current session to expire, they'd run into the same problem. It turns out that python-social-auth relies on the session to store the details of a partial pipeline - which means that if a user tries to resume the pipeline (as in the case of validating their email) from a different browser, ",{"type":23,"tag":2647,"props":12550,"children":12551},{},[12552],{"type":29,"value":12553},"it will fail",{"type":29,"value":386},{"type":23,"tag":32,"props":12556,"children":12557},{},[12558],{"type":29,"value":12559},"The expected user flow here is that the user should be able to validate their email address from any browser, indeed, from any device that can access their email account. They shouldn't need to use the same browser within the arbitrary lifetime of their current server session. Let's fix that.",{"type":23,"tag":32,"props":12561,"children":12562},{},[12563,12565,12571,12573,12580],{"type":29,"value":12564},"Here comes the necessary evil - we'll need to monkey-patch ",{"type":23,"tag":147,"props":12566,"children":12568},{"className":12567},[],[12569],{"type":29,"value":12570},"social.utils.partial_pipeline_data",{"type":29,"value":12572}," in order to allow us to restore the required session data from the stored session in order to resume the pipeline. Our solution takes advantage of the standard storage of ",{"type":23,"tag":36,"props":12574,"children":12577},{"href":12575,"rel":12576},"https://docs.djangoproject.com/en/1.8/topics/http/sessions/#using-database-backed-sessions",[40],[12578],{"type":29,"value":12579},"Django sessions in a database table",{"type":29,"value":12581},", allowing us to interact with the stored sessions in the same way we do other tables.",{"type":23,"tag":32,"props":12583,"children":12584},{},[12585],{"type":29,"value":12586},"We confirm the cryptographic signature on the details within the url parameter, fetch the necessary table row for the session using the session_key, get the needed fields from the row, and presto, we can now properly resume the pipeline.",{"type":23,"tag":141,"props":12588,"children":12590},{"className":143,"code":12589,"language":12,"meta":8,"style":8},"# Monkey patching - an occasionally necessary evil.\nfrom social import utils\nfrom social.exceptions import InvalidEmail\n\nfrom django.core import signing\nfrom django.core.signing import BadSignature\nfrom django.contrib.sessions.models import Session\nfrom django.conf import settings\n\ndef partial_pipeline_data(backend, user=None, *args, **kwargs):\n    \"\"\"\n    Monkey-patch utils.partial_pipeline_data to enable us to retrieve session data by signature key in request.\n    This is necessary to allow users to follow a link in an email to validate their account from a different\n    browser than the one they were using to sign up for the account, or after they've closed/re-opened said\n    browser and potentially flushed their cookies. By adding the session key to a signed base64 encoded signature\n    on the email request, we can retrieve the necessary details from our Django session table.\n    We fetch only the needed details to complete the pipeline authorization process from the session, to prevent\n    nefarious use.\n    \"\"\"\n    data = backend.strategy.request_data()\n    if 'signature' in data:\n        try:\n            signed_details = signing.loads(data['signature'], key=settings.EMAIL_SECRET_KEY)\n            session = Session.objects.get(pk=signed_details['session_key'])\n        except BadSignature, Session.DoesNotExist:\n            raise InvalidEmail(backend)\n\n        session_details = session.get_decoded()\n        backend.strategy.session_set('email_validation_address', session_details['email_validation_address'])\n        backend.strategy.session_set('next', session_details.get('next'))\n        backend.strategy.session_set('partial_pipeline', session_details['partial_pipeline'])\n        backend.strategy.session_set(backend.name + '_state', session_details.get(backend.name + '_state'))\n        backend.strategy.session_set(backend.name + 'unauthorized_token_name',\n                                     session_details.get(backend.name + 'unauthorized_token_name'))\n\n    partial = backend.strategy.session_get('partial_pipeline', None)\n    if partial:\n        idx, backend_name, xargs, xkwargs = \\\n            backend.strategy.partial_from_session(partial)\n        if backend_name == backend.name:\n            kwargs.setdefault('pipeline_index', idx)\n            if user:  # don't update user if it's None\n                kwargs.setdefault('user', user)\n            kwargs.setdefault('request', backend.strategy.request_data())\n            xkwargs.update(kwargs)\n            return xargs, xkwargs\n        else:\n            backend.strategy.clean_partial_pipeline()\nutils.partial_pipeline_data = partial_pipeline_data\nViewport\nWindow\n",[12591],{"type":23,"tag":147,"props":12592,"children":12593},{"__ignoreMap":8},[12594,12602,12623,12644,12651,12670,12691,12712,12733,12740,12785,12792,12800,12808,12816,12824,12832,12840,12848,12855,12872,12892,12904,12950,12990,13003,13016,13023,13040,13066,13092,13115,13150,13170,13190,13197,13230,13242,13259,13267,13288,13306,13324,13342,13359,13367,13379,13390,13398,13415,13423],{"type":23,"tag":151,"props":12595,"children":12596},{"class":153,"line":154},[12597],{"type":23,"tag":151,"props":12598,"children":12599},{"style":324},[12600],{"type":29,"value":12601},"# Monkey patching - an occasionally necessary evil.\n",{"type":23,"tag":151,"props":12603,"children":12604},{"class":153,"line":180},[12605,12609,12614,12618],{"type":23,"tag":151,"props":12606,"children":12607},{"style":158},[12608],{"type":29,"value":161},{"type":23,"tag":151,"props":12610,"children":12611},{"style":164},[12612],{"type":29,"value":12613}," social ",{"type":23,"tag":151,"props":12615,"children":12616},{"style":158},[12617],{"type":29,"value":172},{"type":23,"tag":151,"props":12619,"children":12620},{"style":164},[12621],{"type":29,"value":12622}," utils\n",{"type":23,"tag":151,"props":12624,"children":12625},{"class":153,"line":190},[12626,12630,12635,12639],{"type":23,"tag":151,"props":12627,"children":12628},{"style":158},[12629],{"type":29,"value":161},{"type":23,"tag":151,"props":12631,"children":12632},{"style":164},[12633],{"type":29,"value":12634}," social.exceptions ",{"type":23,"tag":151,"props":12636,"children":12637},{"style":158},[12638],{"type":29,"value":172},{"type":23,"tag":151,"props":12640,"children":12641},{"style":164},[12642],{"type":29,"value":12643}," InvalidEmail\n",{"type":23,"tag":151,"props":12645,"children":12646},{"class":153,"line":220},[12647],{"type":23,"tag":151,"props":12648,"children":12649},{"emptyLinePlaceholder":184},[12650],{"type":29,"value":187},{"type":23,"tag":151,"props":12652,"children":12653},{"class":153,"line":240},[12654,12658,12662,12666],{"type":23,"tag":151,"props":12655,"children":12656},{"style":158},[12657],{"type":29,"value":161},{"type":23,"tag":151,"props":12659,"children":12660},{"style":164},[12661],{"type":29,"value":11923},{"type":23,"tag":151,"props":12663,"children":12664},{"style":158},[12665],{"type":29,"value":172},{"type":23,"tag":151,"props":12667,"children":12668},{"style":164},[12669],{"type":29,"value":11932},{"type":23,"tag":151,"props":12671,"children":12672},{"class":153,"line":254},[12673,12677,12682,12686],{"type":23,"tag":151,"props":12674,"children":12675},{"style":158},[12676],{"type":29,"value":161},{"type":23,"tag":151,"props":12678,"children":12679},{"style":164},[12680],{"type":29,"value":12681}," django.core.signing ",{"type":23,"tag":151,"props":12683,"children":12684},{"style":158},[12685],{"type":29,"value":172},{"type":23,"tag":151,"props":12687,"children":12688},{"style":164},[12689],{"type":29,"value":12690}," BadSignature\n",{"type":23,"tag":151,"props":12692,"children":12693},{"class":153,"line":263},[12694,12698,12703,12707],{"type":23,"tag":151,"props":12695,"children":12696},{"style":158},[12697],{"type":29,"value":161},{"type":23,"tag":151,"props":12699,"children":12700},{"style":164},[12701],{"type":29,"value":12702}," django.contrib.sessions.models ",{"type":23,"tag":151,"props":12704,"children":12705},{"style":158},[12706],{"type":29,"value":172},{"type":23,"tag":151,"props":12708,"children":12709},{"style":164},[12710],{"type":29,"value":12711}," Session\n",{"type":23,"tag":151,"props":12713,"children":12714},{"class":153,"line":272},[12715,12719,12724,12728],{"type":23,"tag":151,"props":12716,"children":12717},{"style":158},[12718],{"type":29,"value":161},{"type":23,"tag":151,"props":12720,"children":12721},{"style":164},[12722],{"type":29,"value":12723}," django.conf ",{"type":23,"tag":151,"props":12725,"children":12726},{"style":158},[12727],{"type":29,"value":172},{"type":23,"tag":151,"props":12729,"children":12730},{"style":164},[12731],{"type":29,"value":12732}," settings\n",{"type":23,"tag":151,"props":12734,"children":12735},{"class":153,"line":296},[12736],{"type":23,"tag":151,"props":12737,"children":12738},{"emptyLinePlaceholder":184},[12739],{"type":29,"value":187},{"type":23,"tag":151,"props":12741,"children":12742},{"class":153,"line":320},[12743,12747,12752,12757,12761,12765,12769,12773,12777,12781],{"type":23,"tag":151,"props":12744,"children":12745},{"style":158},[12746],{"type":29,"value":794},{"type":23,"tag":151,"props":12748,"children":12749},{"style":199},[12750],{"type":29,"value":12751}," partial_pipeline_data",{"type":23,"tag":151,"props":12753,"children":12754},{"style":164},[12755],{"type":29,"value":12756},"(backend, user",{"type":23,"tag":151,"props":12758,"children":12759},{"style":158},[12760],{"type":29,"value":544},{"type":23,"tag":151,"props":12762,"children":12763},{"style":224},[12764],{"type":29,"value":11074},{"type":23,"tag":151,"props":12766,"children":12767},{"style":164},[12768],{"type":29,"value":370},{"type":23,"tag":151,"props":12770,"children":12771},{"style":158},[12772],{"type":29,"value":11097},{"type":23,"tag":151,"props":12774,"children":12775},{"style":164},[12776],{"type":29,"value":11102},{"type":23,"tag":151,"props":12778,"children":12779},{"style":158},[12780],{"type":29,"value":11107},{"type":23,"tag":151,"props":12782,"children":12783},{"style":164},[12784],{"type":29,"value":11112},{"type":23,"tag":151,"props":12786,"children":12787},{"class":153,"line":330},[12788],{"type":23,"tag":151,"props":12789,"children":12790},{"style":285},[12791],{"type":29,"value":7645},{"type":23,"tag":151,"props":12793,"children":12794},{"class":153,"line":901},[12795],{"type":23,"tag":151,"props":12796,"children":12797},{"style":285},[12798],{"type":29,"value":12799},"    Monkey-patch utils.partial_pipeline_data to enable us to retrieve session data by signature key in request.\n",{"type":23,"tag":151,"props":12801,"children":12802},{"class":153,"line":922},[12803],{"type":23,"tag":151,"props":12804,"children":12805},{"style":285},[12806],{"type":29,"value":12807},"    This is necessary to allow users to follow a link in an email to validate their account from a different\n",{"type":23,"tag":151,"props":12809,"children":12810},{"class":153,"line":930},[12811],{"type":23,"tag":151,"props":12812,"children":12813},{"style":285},[12814],{"type":29,"value":12815},"    browser than the one they were using to sign up for the account, or after they've closed/re-opened said\n",{"type":23,"tag":151,"props":12817,"children":12818},{"class":153,"line":971},[12819],{"type":23,"tag":151,"props":12820,"children":12821},{"style":285},[12822],{"type":29,"value":12823},"    browser and potentially flushed their cookies. By adding the session key to a signed base64 encoded signature\n",{"type":23,"tag":151,"props":12825,"children":12826},{"class":153,"line":992},[12827],{"type":23,"tag":151,"props":12828,"children":12829},{"style":285},[12830],{"type":29,"value":12831},"    on the email request, we can retrieve the necessary details from our Django session table.\n",{"type":23,"tag":151,"props":12833,"children":12834},{"class":153,"line":1000},[12835],{"type":23,"tag":151,"props":12836,"children":12837},{"style":285},[12838],{"type":29,"value":12839},"    We fetch only the needed details to complete the pipeline authorization process from the session, to prevent\n",{"type":23,"tag":151,"props":12841,"children":12842},{"class":153,"line":1025},[12843],{"type":23,"tag":151,"props":12844,"children":12845},{"style":285},[12846],{"type":29,"value":12847},"    nefarious use.\n",{"type":23,"tag":151,"props":12849,"children":12850},{"class":153,"line":1046},[12851],{"type":23,"tag":151,"props":12852,"children":12853},{"style":285},[12854],{"type":29,"value":7645},{"type":23,"tag":151,"props":12856,"children":12857},{"class":153,"line":1054},[12858,12863,12867],{"type":23,"tag":151,"props":12859,"children":12860},{"style":164},[12861],{"type":29,"value":12862},"    data ",{"type":23,"tag":151,"props":12864,"children":12865},{"style":158},[12866],{"type":29,"value":544},{"type":23,"tag":151,"props":12868,"children":12869},{"style":164},[12870],{"type":29,"value":12871}," backend.strategy.request_data()\n",{"type":23,"tag":151,"props":12873,"children":12874},{"class":153,"line":1091},[12875,12879,12884,12888],{"type":23,"tag":151,"props":12876,"children":12877},{"style":158},[12878],{"type":29,"value":10046},{"type":23,"tag":151,"props":12880,"children":12881},{"style":285},[12882],{"type":29,"value":12883}," 'signature'",{"type":23,"tag":151,"props":12885,"children":12886},{"style":158},[12887],{"type":29,"value":894},{"type":23,"tag":151,"props":12889,"children":12890},{"style":164},[12891],{"type":29,"value":845},{"type":23,"tag":151,"props":12893,"children":12894},{"class":153,"line":1112},[12895,12900],{"type":23,"tag":151,"props":12896,"children":12897},{"style":158},[12898],{"type":29,"value":12899},"        try",{"type":23,"tag":151,"props":12901,"children":12902},{"style":164},[12903],{"type":29,"value":2226},{"type":23,"tag":151,"props":12905,"children":12906},{"class":153,"line":1120},[12907,12912,12916,12921,12926,12930,12934,12938,12942,12946],{"type":23,"tag":151,"props":12908,"children":12909},{"style":164},[12910],{"type":29,"value":12911},"            signed_details ",{"type":23,"tag":151,"props":12913,"children":12914},{"style":158},[12915],{"type":29,"value":544},{"type":23,"tag":151,"props":12917,"children":12918},{"style":164},[12919],{"type":29,"value":12920}," signing.loads(data[",{"type":23,"tag":151,"props":12922,"children":12923},{"style":285},[12924],{"type":29,"value":12925},"'signature'",{"type":23,"tag":151,"props":12927,"children":12928},{"style":164},[12929],{"type":29,"value":959},{"type":23,"tag":151,"props":12931,"children":12932},{"style":764},[12933],{"type":29,"value":12514},{"type":23,"tag":151,"props":12935,"children":12936},{"style":158},[12937],{"type":29,"value":544},{"type":23,"tag":151,"props":12939,"children":12940},{"style":164},[12941],{"type":29,"value":12068},{"type":23,"tag":151,"props":12943,"children":12944},{"style":224},[12945],{"type":29,"value":12073},{"type":23,"tag":151,"props":12947,"children":12948},{"style":164},[12949],{"type":29,"value":293},{"type":23,"tag":151,"props":12951,"children":12952},{"class":153,"line":1129},[12953,12958,12962,12967,12972,12976,12981,12986],{"type":23,"tag":151,"props":12954,"children":12955},{"style":164},[12956],{"type":29,"value":12957},"            session ",{"type":23,"tag":151,"props":12959,"children":12960},{"style":158},[12961],{"type":29,"value":544},{"type":23,"tag":151,"props":12963,"children":12964},{"style":164},[12965],{"type":29,"value":12966}," Session.objects.get(",{"type":23,"tag":151,"props":12968,"children":12969},{"style":764},[12970],{"type":29,"value":12971},"pk",{"type":23,"tag":151,"props":12973,"children":12974},{"style":158},[12975],{"type":29,"value":544},{"type":23,"tag":151,"props":12977,"children":12978},{"style":164},[12979],{"type":29,"value":12980},"signed_details[",{"type":23,"tag":151,"props":12982,"children":12983},{"style":285},[12984],{"type":29,"value":12985},"'session_key'",{"type":23,"tag":151,"props":12987,"children":12988},{"style":164},[12989],{"type":29,"value":1963},{"type":23,"tag":151,"props":12991,"children":12992},{"class":153,"line":1137},[12993,12998],{"type":23,"tag":151,"props":12994,"children":12995},{"style":158},[12996],{"type":29,"value":12997},"        except",{"type":23,"tag":151,"props":12999,"children":13000},{"style":164},[13001],{"type":29,"value":13002}," BadSignature, Session.DoesNotExist:\n",{"type":23,"tag":151,"props":13004,"children":13005},{"class":153,"line":1146},[13006,13011],{"type":23,"tag":151,"props":13007,"children":13008},{"style":158},[13009],{"type":29,"value":13010},"            raise",{"type":23,"tag":151,"props":13012,"children":13013},{"style":164},[13014],{"type":29,"value":13015}," InvalidEmail(backend)\n",{"type":23,"tag":151,"props":13017,"children":13018},{"class":153,"line":1155},[13019],{"type":23,"tag":151,"props":13020,"children":13021},{"emptyLinePlaceholder":184},[13022],{"type":29,"value":187},{"type":23,"tag":151,"props":13024,"children":13025},{"class":153,"line":2034},[13026,13031,13035],{"type":23,"tag":151,"props":13027,"children":13028},{"style":164},[13029],{"type":29,"value":13030},"        session_details ",{"type":23,"tag":151,"props":13032,"children":13033},{"style":158},[13034],{"type":29,"value":544},{"type":23,"tag":151,"props":13036,"children":13037},{"style":164},[13038],{"type":29,"value":13039}," session.get_decoded()\n",{"type":23,"tag":151,"props":13041,"children":13042},{"class":153,"line":2043},[13043,13048,13053,13058,13062],{"type":23,"tag":151,"props":13044,"children":13045},{"style":164},[13046],{"type":29,"value":13047},"        backend.strategy.session_set(",{"type":23,"tag":151,"props":13049,"children":13050},{"style":285},[13051],{"type":29,"value":13052},"'email_validation_address'",{"type":23,"tag":151,"props":13054,"children":13055},{"style":164},[13056],{"type":29,"value":13057},", session_details[",{"type":23,"tag":151,"props":13059,"children":13060},{"style":285},[13061],{"type":29,"value":13052},{"type":23,"tag":151,"props":13063,"children":13064},{"style":164},[13065],{"type":29,"value":1963},{"type":23,"tag":151,"props":13067,"children":13068},{"class":153,"line":2052},[13069,13073,13078,13083,13087],{"type":23,"tag":151,"props":13070,"children":13071},{"style":164},[13072],{"type":29,"value":13047},{"type":23,"tag":151,"props":13074,"children":13075},{"style":285},[13076],{"type":29,"value":13077},"'next'",{"type":23,"tag":151,"props":13079,"children":13080},{"style":164},[13081],{"type":29,"value":13082},", session_details.get(",{"type":23,"tag":151,"props":13084,"children":13085},{"style":285},[13086],{"type":29,"value":13077},{"type":23,"tag":151,"props":13088,"children":13089},{"style":164},[13090],{"type":29,"value":13091},"))\n",{"type":23,"tag":151,"props":13093,"children":13094},{"class":153,"line":2061},[13095,13099,13103,13107,13111],{"type":23,"tag":151,"props":13096,"children":13097},{"style":164},[13098],{"type":29,"value":13047},{"type":23,"tag":151,"props":13100,"children":13101},{"style":285},[13102],{"type":29,"value":11430},{"type":23,"tag":151,"props":13104,"children":13105},{"style":164},[13106],{"type":29,"value":13057},{"type":23,"tag":151,"props":13108,"children":13109},{"style":285},[13110],{"type":29,"value":11430},{"type":23,"tag":151,"props":13112,"children":13113},{"style":164},[13114],{"type":29,"value":1963},{"type":23,"tag":151,"props":13116,"children":13117},{"class":153,"line":2070},[13118,13123,13128,13133,13138,13142,13146],{"type":23,"tag":151,"props":13119,"children":13120},{"style":164},[13121],{"type":29,"value":13122},"        backend.strategy.session_set(backend.name ",{"type":23,"tag":151,"props":13124,"children":13125},{"style":158},[13126],{"type":29,"value":13127},"+",{"type":23,"tag":151,"props":13129,"children":13130},{"style":285},[13131],{"type":29,"value":13132}," '_state'",{"type":23,"tag":151,"props":13134,"children":13135},{"style":164},[13136],{"type":29,"value":13137},", session_details.get(backend.name ",{"type":23,"tag":151,"props":13139,"children":13140},{"style":158},[13141],{"type":29,"value":13127},{"type":23,"tag":151,"props":13143,"children":13144},{"style":285},[13145],{"type":29,"value":13132},{"type":23,"tag":151,"props":13147,"children":13148},{"style":164},[13149],{"type":29,"value":13091},{"type":23,"tag":151,"props":13151,"children":13152},{"class":153,"line":2079},[13153,13157,13161,13166],{"type":23,"tag":151,"props":13154,"children":13155},{"style":164},[13156],{"type":29,"value":13122},{"type":23,"tag":151,"props":13158,"children":13159},{"style":158},[13160],{"type":29,"value":13127},{"type":23,"tag":151,"props":13162,"children":13163},{"style":285},[13164],{"type":29,"value":13165}," 'unauthorized_token_name'",{"type":23,"tag":151,"props":13167,"children":13168},{"style":164},[13169],{"type":29,"value":1805},{"type":23,"tag":151,"props":13171,"children":13172},{"class":153,"line":2088},[13173,13178,13182,13186],{"type":23,"tag":151,"props":13174,"children":13175},{"style":164},[13176],{"type":29,"value":13177},"                                     session_details.get(backend.name ",{"type":23,"tag":151,"props":13179,"children":13180},{"style":158},[13181],{"type":29,"value":13127},{"type":23,"tag":151,"props":13183,"children":13184},{"style":285},[13185],{"type":29,"value":13165},{"type":23,"tag":151,"props":13187,"children":13188},{"style":164},[13189],{"type":29,"value":13091},{"type":23,"tag":151,"props":13191,"children":13192},{"class":153,"line":2097},[13193],{"type":23,"tag":151,"props":13194,"children":13195},{"emptyLinePlaceholder":184},[13196],{"type":29,"value":187},{"type":23,"tag":151,"props":13198,"children":13199},{"class":153,"line":2106},[13200,13205,13209,13214,13218,13222,13226],{"type":23,"tag":151,"props":13201,"children":13202},{"style":164},[13203],{"type":29,"value":13204},"    partial ",{"type":23,"tag":151,"props":13206,"children":13207},{"style":158},[13208],{"type":29,"value":544},{"type":23,"tag":151,"props":13210,"children":13211},{"style":164},[13212],{"type":29,"value":13213}," backend.strategy.session_get(",{"type":23,"tag":151,"props":13215,"children":13216},{"style":285},[13217],{"type":29,"value":11430},{"type":23,"tag":151,"props":13219,"children":13220},{"style":164},[13221],{"type":29,"value":370},{"type":23,"tag":151,"props":13223,"children":13224},{"style":224},[13225],{"type":29,"value":11074},{"type":23,"tag":151,"props":13227,"children":13228},{"style":164},[13229],{"type":29,"value":293},{"type":23,"tag":151,"props":13231,"children":13232},{"class":153,"line":2115},[13233,13237],{"type":23,"tag":151,"props":13234,"children":13235},{"style":158},[13236],{"type":29,"value":10046},{"type":23,"tag":151,"props":13238,"children":13239},{"style":164},[13240],{"type":29,"value":13241}," partial:\n",{"type":23,"tag":151,"props":13243,"children":13244},{"class":153,"line":2124},[13245,13250,13254],{"type":23,"tag":151,"props":13246,"children":13247},{"style":164},[13248],{"type":29,"value":13249},"        idx, backend_name, xargs, xkwargs ",{"type":23,"tag":151,"props":13251,"children":13252},{"style":158},[13253],{"type":29,"value":544},{"type":23,"tag":151,"props":13255,"children":13256},{"style":164},[13257],{"type":29,"value":13258}," \\\n",{"type":23,"tag":151,"props":13260,"children":13261},{"class":153,"line":2133},[13262],{"type":23,"tag":151,"props":13263,"children":13264},{"style":164},[13265],{"type":29,"value":13266},"            backend.strategy.partial_from_session(partial)\n",{"type":23,"tag":151,"props":13268,"children":13269},{"class":153,"line":2142},[13270,13274,13279,13283],{"type":23,"tag":151,"props":13271,"children":13272},{"style":158},[13273],{"type":29,"value":9360},{"type":23,"tag":151,"props":13275,"children":13276},{"style":164},[13277],{"type":29,"value":13278}," backend_name ",{"type":23,"tag":151,"props":13280,"children":13281},{"style":158},[13282],{"type":29,"value":9370},{"type":23,"tag":151,"props":13284,"children":13285},{"style":164},[13286],{"type":29,"value":13287}," backend.name:\n",{"type":23,"tag":151,"props":13289,"children":13290},{"class":153,"line":2151},[13291,13296,13301],{"type":23,"tag":151,"props":13292,"children":13293},{"style":164},[13294],{"type":29,"value":13295},"            kwargs.setdefault(",{"type":23,"tag":151,"props":13297,"children":13298},{"style":285},[13299],{"type":29,"value":13300},"'pipeline_index'",{"type":23,"tag":151,"props":13302,"children":13303},{"style":164},[13304],{"type":29,"value":13305},", idx)\n",{"type":23,"tag":151,"props":13307,"children":13308},{"class":153,"line":2160},[13309,13314,13319],{"type":23,"tag":151,"props":13310,"children":13311},{"style":158},[13312],{"type":29,"value":13313},"            if",{"type":23,"tag":151,"props":13315,"children":13316},{"style":164},[13317],{"type":29,"value":13318}," user:  ",{"type":23,"tag":151,"props":13320,"children":13321},{"style":324},[13322],{"type":29,"value":13323},"# don't update user if it's None\n",{"type":23,"tag":151,"props":13325,"children":13326},{"class":153,"line":2169},[13327,13332,13337],{"type":23,"tag":151,"props":13328,"children":13329},{"style":164},[13330],{"type":29,"value":13331},"                kwargs.setdefault(",{"type":23,"tag":151,"props":13333,"children":13334},{"style":285},[13335],{"type":29,"value":13336},"'user'",{"type":23,"tag":151,"props":13338,"children":13339},{"style":164},[13340],{"type":29,"value":13341},", user)\n",{"type":23,"tag":151,"props":13343,"children":13344},{"class":153,"line":2178},[13345,13349,13354],{"type":23,"tag":151,"props":13346,"children":13347},{"style":164},[13348],{"type":29,"value":13295},{"type":23,"tag":151,"props":13350,"children":13351},{"style":285},[13352],{"type":29,"value":13353},"'request'",{"type":23,"tag":151,"props":13355,"children":13356},{"style":164},[13357],{"type":29,"value":13358},", backend.strategy.request_data())\n",{"type":23,"tag":151,"props":13360,"children":13361},{"class":153,"line":2192},[13362],{"type":23,"tag":151,"props":13363,"children":13364},{"style":164},[13365],{"type":29,"value":13366},"            xkwargs.update(kwargs)\n",{"type":23,"tag":151,"props":13368,"children":13369},{"class":153,"line":2200},[13370,13374],{"type":23,"tag":151,"props":13371,"children":13372},{"style":158},[13373],{"type":29,"value":9396},{"type":23,"tag":151,"props":13375,"children":13376},{"style":164},[13377],{"type":29,"value":13378}," xargs, xkwargs\n",{"type":23,"tag":151,"props":13380,"children":13381},{"class":153,"line":2229},[13382,13386],{"type":23,"tag":151,"props":13383,"children":13384},{"style":158},[13385],{"type":29,"value":10182},{"type":23,"tag":151,"props":13387,"children":13388},{"style":164},[13389],{"type":29,"value":2226},{"type":23,"tag":151,"props":13391,"children":13392},{"class":153,"line":5420},[13393],{"type":23,"tag":151,"props":13394,"children":13395},{"style":164},[13396],{"type":29,"value":13397},"            backend.strategy.clean_partial_pipeline()\n",{"type":23,"tag":151,"props":13399,"children":13400},{"class":153,"line":9390},[13401,13406,13410],{"type":23,"tag":151,"props":13402,"children":13403},{"style":164},[13404],{"type":29,"value":13405},"utils.partial_pipeline_data ",{"type":23,"tag":151,"props":13407,"children":13408},{"style":158},[13409],{"type":29,"value":544},{"type":23,"tag":151,"props":13411,"children":13412},{"style":164},[13413],{"type":29,"value":13414}," partial_pipeline_data\n",{"type":23,"tag":151,"props":13416,"children":13417},{"class":153,"line":9403},[13418],{"type":23,"tag":151,"props":13419,"children":13420},{"style":164},[13421],{"type":29,"value":13422},"Viewport\n",{"type":23,"tag":151,"props":13424,"children":13425},{"class":153,"line":9411},[13426],{"type":23,"tag":151,"props":13427,"children":13428},{"style":164},[13429],{"type":29,"value":13430},"Window\n",{"type":23,"tag":24,"props":13432,"children":13434},{"id":13433},"step-4-relax-with-hot-beverage-of-choice",[13435],{"type":29,"value":13436},"Step 4 - Relax with hot beverage of choice",{"type":23,"tag":32,"props":13438,"children":13439},{},[13440],{"type":29,"value":13441},"Optional, but recommended.",{"type":23,"tag":2836,"props":13443,"children":13444},{},[13445],{"type":29,"value":2840},{"title":8,"searchDepth":190,"depth":190,"links":13447},[13448,13449,13452,13453,13454],{"id":10582,"depth":180,"text":10585},{"id":10639,"depth":180,"text":10642,"children":13450},[13451],{"id":11810,"depth":220,"text":11813},{"id":11816,"depth":180,"text":11819},{"id":12526,"depth":180,"text":12529},{"id":13433,"depth":180,"text":13436},"content:ckeefer:2015-3:EmailValidation.md","ckeefer/2015-3/EmailValidation.md","ckeefer/2015-3/EmailValidation",{"user":5523,"name":5524},1780330265474]