<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: Taranjeet Singh</title>
    <description>The latest articles on Forem by Taranjeet Singh (@taranjeet).</description>
    <link>https://forem.com/taranjeet</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F70570%2F9e08e00f-3c7a-48f0-97ea-adb851d357b4.jpeg</url>
      <title>Forem: Taranjeet Singh</title>
      <link>https://forem.com/taranjeet</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/taranjeet"/>
    <language>en</language>
    <item>
      <title>Behind the Scenes: How LangChain calculates OpenAI's pricing?</title>
      <dc:creator>Taranjeet Singh</dc:creator>
      <pubDate>Thu, 01 Jun 2023 11:38:35 +0000</pubDate>
      <link>https://forem.com/taranjeet/behind-the-scenes-how-langchain-calculates-openais-pricing-155p</link>
      <guid>https://forem.com/taranjeet/behind-the-scenes-how-langchain-calculates-openais-pricing-155p</guid>
      <description>&lt;h2&gt;
  
  
  tl;dr
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;This post covers how LangChain calculates pricing when one uses OpenAI’s LLM.&lt;/li&gt;
&lt;li&gt;There are two functions that help in this. The first function maintains the model cost mapping. The second function calculates the cost given a response from OpenAI’s API.&lt;/li&gt;
&lt;li&gt;Some insights on why LangChain exists and how it is helpful for developers.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Today’s post is a dive into the source code of a popular open-source package called &lt;a href="https://github.com/hwchase17/langchain"&gt;LangChain&lt;/a&gt;. It’s a framework for developing applications powered by language models.&lt;/p&gt;

&lt;p&gt;We will cover how LangChain calculates the OpenAI cost given the response.&lt;/p&gt;

&lt;p&gt;There is a &lt;code&gt;get_openai_callback&lt;/code&gt; function from &lt;code&gt;langchain.callbacks&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://python.langchain.com/en/latest/modules/models/llms/examples/token_usage_tracking.html"&gt;This page&lt;/a&gt; in LangChain docs lists how it can be used&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;from langchain.llms import OpenAI
from langchain.callbacks import get_openai_callback

llm = OpenAI(model_name="text-davinci-002", n=2, best_of=2)

with get_openai_callback() as cb:
    result = llm("Tell me a joke")
    print(cb)


Tokens Used: 42
    Prompt Tokens: 4
    Completion Tokens: 38
Successful Requests: 1
Total Cost (USD): $0.00084
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here the model name is &lt;code&gt;text-davinci-002&lt;/code&gt; (LangChain team if you are reading this - doc needs an update), but the same will work with &lt;code&gt;gpt-3.5-turbo&lt;/code&gt; or &lt;code&gt;gpt-4&lt;/code&gt; .&lt;/p&gt;

&lt;p&gt;I was more interested in the source code, so I started digging into the code base. The source code for this function exists in &lt;code&gt;langchain/callbacks/openai_info.py&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Here is the first function defined in the file - &lt;code&gt;get_openai_model_cost_per_1k_tokens&lt;/code&gt;. Below you can find its source code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;def get_openai_model_cost_per_1k_tokens(
    model_name: str, is_completion: bool = False
) -&amp;gt; float:
    model_cost_mapping = {
        "gpt-4": 0.03,
        "gpt-4-0314": 0.03,
        "gpt-4-completion": 0.06,
        "gpt-4-0314-completion": 0.06,
        "gpt-4-32k": 0.06,
        "gpt-4-32k-0314": 0.06,
        "gpt-4-32k-completion": 0.12,
        "gpt-4-32k-0314-completion": 0.12,
        "gpt-3.5-turbo": 0.002,
        "gpt-3.5-turbo-0301": 0.002,
        "text-ada-001": 0.0004,
        "ada": 0.0004,
        "text-babbage-001": 0.0005,
        "babbage": 0.0005,
        "text-curie-001": 0.002,
        "curie": 0.002,
        "text-davinci-003": 0.02,
        "text-davinci-002": 0.02,
        "code-davinci-002": 0.02,
    }

    cost = model_cost_mapping.get(
        model_name.lower()
        + ("-completion" if is_completion and model_name.startswith("gpt-4") else ""),
        None,
    )
    if cost is None:
        raise ValueError(
            f"Unknown model: {model_name}. Please provide a valid OpenAI model name."
            "Known models are: " + ", ".join(model_cost_mapping.keys())
        )

    return cost
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So this is the function which is maintaining the entire cost given a model name. Let’s say OpenAI adds a new model ( &lt;code&gt;gpt-5&lt;/code&gt;  maybe? or &lt;code&gt;gpt-4-turbo&lt;/code&gt;), then this is the place its pricing will get stored.&lt;/p&gt;

&lt;p&gt;Also, note that this function has a &lt;code&gt;is_completion&lt;/code&gt; flag. This flag is used to pick the cost of completion or other models.&lt;/p&gt;

&lt;p&gt;For ease, you can consider that the above function is maintaining a mapping of model_name and its cost per 1000 tokens, called as &lt;code&gt;model_cost_mapping&lt;/code&gt; here onwards&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;model_cost_mapping = {
    "gpt-3.5-turbo": 0.002,
    "gpt-4": 0.03
} 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let’s see the other code in this file. There is a class called &lt;code&gt;OpenAICallbackHandler&lt;/code&gt; and in it, there is a function called &lt;code&gt;on_llm_end&lt;/code&gt; . I think that this function must be called at the end when the response from LLM is generated completely. Let’s verify this. I searched for the reference of this function and found out that &lt;code&gt;on_llm_end&lt;/code&gt; is called in &lt;code&gt;generate&lt;/code&gt;  function in &lt;code&gt;BaseLLM&lt;/code&gt; class defined in &lt;code&gt;llms/base.py&lt;/code&gt;. It gets called after the response from LLM is generated.&lt;/p&gt;

&lt;p&gt;Now, let’s dig into &lt;code&gt;on_llm_end&lt;/code&gt; and see how it’s using the &lt;code&gt;model_cost_mapping&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;def on_llm_end(self, response: LLMResult, **kwargs: Any) -&amp;gt; None:
        """Collect token usage."""
        if response.llm_output is not None:
            self.successful_requests += 1
            if "token_usage" in response.llm_output:
                token_usage = response.llm_output["token_usage"]
                if "model_name" in response.llm_output:
                    completion_cost = get_openai_model_cost_per_1k_tokens(
                        response.llm_output["model_name"], is_completion=True
                    ) * (token_usage.get("completion_tokens", 0) / 1000)
                    prompt_cost = get_openai_model_cost_per_1k_tokens(
                        response.llm_output["model_name"]
                    ) * (token_usage.get("prompt_tokens", 0) / 1000)

                    self.total_cost += prompt_cost + completion_cost

                if "total_tokens" in token_usage:
                    self.total_tokens += token_usage["total_tokens"]
                if "prompt_tokens" in token_usage:
                    self.prompt_tokens += token_usage["prompt_tokens"]
                if "completion_tokens" in token_usage:
                    self.completion_tokens += token_usage["completion_tokens"]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let’s understand what the above function is doing. Before proceeding, let’s see how a sample response from OpenAI API looks like&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;gt;&amp;gt;&amp;gt; import openai

&amp;gt;&amp;gt;&amp;gt; openai.api_key = 'sk-&amp;lt;your key here&amp;gt;'
&amp;gt;&amp;gt;&amp;gt; response = openai.ChatCompletion.create(model="gpt-3.5-turbo", messages=[{"role":"user", "content": "hey, how are you?"}], temperature=0.7)

&amp;gt;&amp;gt;&amp;gt; print(response)
{
  "choices": [
    {
      "finish_reason": "stop",
      "index": 0,
      "message": {
        "content": "As an AI language model, I do not have emotions, but I'm functioning properly. How can I assist you today?",
        "role": "assistant"
      }
    }
  ],
  "created": 1685475109,
  "id": "chatcmpl-7LzMLNGeUTvJsi0eJYrdrZow0DRnU",
  "model": "gpt-3.5-turbo-0301",
  "object": "chat.completion",
  "usage": {
    "completion_tokens": 25,
    "prompt_tokens": 14,
    "total_tokens": 39
  }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is interesting. In the response received from OpenAI, I am getting token details in &lt;code&gt;usage&lt;/code&gt; key whereas in LangChain, the code is looking for &lt;code&gt;token_usage&lt;/code&gt;. &lt;/p&gt;

&lt;p&gt;I did a little digging here and found out that LangChain extracts the &lt;code&gt;usage&lt;/code&gt; key only but puts it in a &lt;code&gt;token_usage&lt;/code&gt; key in &lt;code&gt;chat_models/openai.py&lt;/code&gt;. Here is the relevant code snippet&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# langchain/chat_models/openai.py

def _create_chat_result(self, response: Mapping[str, Any]) -&amp;gt; ChatResult:
    generations = []
    for res in response["choices"]:
        message = _convert_dict_to_message(res["message"])
        gen = ChatGeneration(message=message)
        generations.append(gen)
    llm_output = {"token_usage": response["usage"], "model_name": self.model_name}
    return ChatResult(generations=generations, llm_output=llm_output)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;An interesting piece of thought here is why LangChain does this.&lt;/p&gt;

&lt;p&gt;LangChain does this so that it can easily maintain interoperability between various LLM providers and expose a unified interface to all the developers. This is one of the main reasons why developers love LangChain a lot. &lt;/p&gt;

&lt;p&gt;Imagine Anthropic’s model started performing better one day than GPT-4, all you have to do is just change the provider from LangChain and everything continues to function as normal. If you are not using LangChain, then you will have to implement a lot of logic, input, and output format changes at your end, thereby increasing your work and introducing more errors in a production environment.&lt;/p&gt;

&lt;p&gt;Coming back to the cost calculation piece now, we can see the function on_llm_end is very straightforward. It checks for relevant keys in the response and then calculates the cost using &lt;code&gt;model_cost_mapping&lt;/code&gt;. &lt;/p&gt;

&lt;p&gt;I am sharing a Python code snippet on the basis of the above code. This will be helpful in case you want to just use the cost calculation logic given an OpenAI response at your end.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# credits: https://github.com/hwchase17/langchain/blob/master/langchain/callbacks/openai_info.py

&amp;gt;&amp;gt;&amp;gt; import openai

&amp;gt;&amp;gt;&amp;gt; def get_openai_model_cost_table(model_name='gpt-3.5-turbo', is_completion=False):
    model_cost_mapping = {
        "gpt-4": 0.03,
        "gpt-4-0314": 0.03,
        "gpt-4-completion": 0.06,
        "gpt-4-0314-completion": 0.06,
        "gpt-4-32k": 0.06,
        "gpt-4-32k-0314": 0.06,
        "gpt-4-32k-completion": 0.12,
        "gpt-4-32k-0314-completion": 0.12,
        "gpt-3.5-turbo": 0.002,
        "gpt-3.5-turbo-0301": 0.002,
        "text-ada-001": 0.0004,
        "ada": 0.0004,
        "text-babbage-001": 0.0005,
        "babbage": 0.0005,
        "text-curie-001": 0.002,
        "curie": 0.002,
        "text-davinci-003": 0.02,
        "text-davinci-002": 0.02,
        "code-davinci-002": 0.02,
    }
    cost = model_cost_mapping.get(
        model_name.lower()
        + ("-completion" if is_completion and model_name.startswith("gpt-4") else ""),
        None,
    )
    if cost is None:
        raise ValueError(
            f"Unknown model: {model_name}. Please provide a valid OpenAI model name."
            "Known models are: " + ", ".join(model_cost_mapping.keys())
        )
    return cost


&amp;gt;&amp;gt;&amp;gt; def get_openai_cost(response):
    """
    Pass openai response object and get total cost.
    """
    total_cost = 0
    if "usage" in response:
        completion_cost = get_openai_model_cost_table(
            model_name=response["model"],
            is_completion=True,
        ) * (response["usage"].get("completion_tokens", 0) / 1000.0)
        prompt_cost = get_openai_model_cost_table(
            model_name=response["model"]
        ) * (response["usage"].get("prompt_tokens", 0) / 1000.0)
        total_cost = prompt_cost + completion_cost
    return total_cost



&amp;gt;&amp;gt;&amp;gt; openai.api_key = 'sk-&amp;lt;your key here&amp;gt;'
&amp;gt;&amp;gt;&amp;gt; response = openai.ChatCompletion.create(model="gpt-3.5-turbo", messages=[{"role":"user", "content": "hey, how are you?"}], temperature=0.7)

&amp;gt;&amp;gt;&amp;gt; print("Total cost: ", get_openai_cost(response))
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you want to read any past issues, check them out &lt;a href="https://kuwai.beehiiv.com/"&gt;here&lt;/a&gt;. &lt;/p&gt;

&lt;p&gt;If you have any feedback/issues etc, reply here or &lt;a href="https://twitter.com/taranjeetio"&gt;DM me&lt;/a&gt; on Twitter.&lt;/p&gt;

</description>
      <category>python</category>
      <category>opensource</category>
      <category>openai</category>
      <category>ai</category>
    </item>
    <item>
      <title>How to build your own OpenAI cost calculator</title>
      <dc:creator>Taranjeet Singh</dc:creator>
      <pubDate>Wed, 31 May 2023 06:33:42 +0000</pubDate>
      <link>https://forem.com/taranjeet/how-to-build-your-own-openai-cost-calculator-4ia</link>
      <guid>https://forem.com/taranjeet/how-to-build-your-own-openai-cost-calculator-4ia</guid>
      <description>&lt;h2&gt;
  
  
  tl;dr
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;This post covers 2 network calls made on &lt;a href="http://platform.openai.com/"&gt;platform.openai.com&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;The first network call gives the subscription details and can be made using an OPENAI_API_TOKEN
&lt;/li&gt;
&lt;li&gt;The second network call gives the cost details for a particular period and can be made using an OPENAI_API_TOKEN&lt;/li&gt;
&lt;li&gt;There is a curl call, python code, and a bonus curl call for Credit grant details (but it only works from the browser)&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/Keeping-Up-With-AI/your-own-openai-cost-calculator"&gt;GitHub code&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In this post, we will learn how you can build your own OpenAI cost calculator.&lt;/p&gt;

&lt;h2&gt;
  
  
  OpenAI Subscription Details
&lt;/h2&gt;

&lt;p&gt;I discovered a subscription network call from the Network tab of Chrome Dev tools.&lt;br&gt;
There is a call being made to the subscription endpoint. Here is a curl.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;curl 'https://api.openai.com/dashboard/billing/subscription'
  -H 'authority: api.openai.com'
  -H 'accept: */*'
  -H 'accept-language: en-GB,en-US;q=0.9,en;q=0.8'
  -H 'authorization: Bearer sess-${SESSION_ID}'
  -H 'openai-organization: org-${ORG_ID}'
  -H 'origin: https://platform.openai.com'
  -H 'referer: https://platform.openai.com/'
  -H 'sec-fetch-dest: empty'
  -H 'sec-fetch-mode: cors'
  -H 'sec-fetch-site: same-site'
  -H 'sec-gpc: 1'
  -H 'user-agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/103.0.5060.53 Safari/537.36'
  --compressed
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;and here is the response from the above curl&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{
  "object": "billing_subscription",
  "has_payment_method": true,
  "canceled": false,
  "canceled_at": null,
  "delinquent": null,
  "access_until": 1685249945,
  "soft_limit": 1000000,
  "hard_limit": 2000000,
  "system_hard_limit": 2000000,
  "soft_limit_usd": 100.0,
  "hard_limit_usd": 120.0,
  "system_hard_limit_usd": 120.0,
  "plan": {
    "title": "Pay-as-you-go",
    "id": "payg"
  },
  "account_name": null,
  "po_number": null,
  "billing_email": null,
  "tax_ids": null,
  "billing_address": {
    "city": "city_name",
    "line1": "address line 1",
    "line2": "address line 2",
    "state": "state",
    "country": "IN",
    "postal_code": "111111"
  },
  "business_address": null
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Note the session sess-{session_id} in the curl call is obtained from the login call. This is a bit complicated process where in you first have to make the login call and then get the session id. Also, high chance that your login call won’t get the correct response because of the bot prevention mechanism applied.&lt;/p&gt;

&lt;p&gt;I started playing around and the first thing that I did was replacing sess-{session_id} with my OPENAI_API_KEY. And it worked like magic!&lt;/p&gt;

&lt;p&gt;So, here is the subscription call curl with OPENAI_API_KEY (eg: sk-)&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;curl 'https://api.openai.com/dashboard/billing/subscription'
  -H 'authority: api.openai.com'
  -H 'accept: */*'
  -H 'accept-language: en-GB,en-US;q=0.9,en;q=0.8'
  -H 'authorization: Bearer ${OPENAI_API_KEY}'
  -H 'openai-organization: org-${ORG_ID}'
  -H 'origin: https://platform.openai.com'
  -H 'referer: https://platform.openai.com/'
  -H 'sec-fetch-dest: empty'
  -H 'sec-fetch-mode: cors'
  -H 'sec-fetch-site: same-site'
  -H 'sec-gpc: 1'
  -H 'user-agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/103.0.5060.53 Safari/537.36'
  --compressed
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The response for this curl call is the same.&lt;/p&gt;

&lt;p&gt;The subscription call is useful to get billing limits (soft and hard), plan details, and billing account details and addresses.&lt;/p&gt;

&lt;p&gt;Python code to get subscription details&lt;/p&gt;

&lt;p&gt;Here is a Python function that you can call from your app. You just need to pass the OPENAI_API_TOKEN. ORG_ID is optional&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import requests

def get_openai_subscription_details(api_key, org_id=None):
    '''
    Get subscription details from OpenAI
    '''
    headers = {
        'authority': 'api.openai.com',
        'accept': '*/*',
        'accept-language': 'en-GB,en-US;q=0.9,en;q=0.8',
        'authorization': f'Bearer {api_key}',
        'origin': 'https://platform.openai.com',
        'referer': 'https://platform.openai.com/',
        'sec-fetch-dest': 'empty',
        'sec-fetch-mode': 'cors',
        'sec-fetch-site': 'same-site',
        'sec-gpc': '1',
        'user-agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/103.0.5060.53 Safari/537.36',

    }
    if org_id:
        headers['openai-organization'] = f'org-{org_id}'
    response = requests.get('https://api.openai.com/dashboard/billing/subscription', headers=headers)
    return response.json()

OPENAI_API_KEY = ''     # set the one for which you want to calculate cost

print(get_openai_subscription_details(OPENAI_API_KEY))
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;OpenAI Cost Details&lt;/p&gt;

&lt;p&gt;But how do I get the cost that I have spent so far?&lt;/p&gt;

&lt;p&gt;So I went to the Usage tab, where OpenAI shows a graph and tabular view of all the API calls being made, usage, and credit details. From this tab, I discovered an usage endpoint. This is exactly what we need to build the OpenAI cost calculator.&lt;/p&gt;

&lt;p&gt;Here is the curl call:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;curl 'https://api.openai.com/dashboard/billing/usage?end_date=2023-06-01&amp;amp;start_date=2023-05-01'
  -H 'authority: api.openai.com'
  -H 'accept: */*'
  -H 'accept-language: en-GB,en-US;q=0.9,en;q=0.8'
  -H 'authorization: Bearer ${OPENAI_API_KEY}'
  -H 'openai-organization: org-${ORG_ID}'
  -H 'origin: https://platform.openai.com'
  -H 'referer: https://platform.openai.com/'
  -H 'sec-fetch-dest: empty'
  -H 'sec-fetch-mode: cors'
  -H 'sec-fetch-site: same-site'
  -H 'sec-gpc: 1'
  -H 'user-agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/103.0.5060.53 Safari/537.36'
  --compressed
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;and the response from the above call&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{
  "object": "list",
  "daily_costs": [
    {
      "timestamp": 1682899200.0,
      "line_items": [
        {
          "name": "Instruct models",
          "cost": 0.00144
        },
        {
          "name": "Chat models",
          "cost": 26.369
        },
        {
          "name": "GPT-4",
          "cost": 111.978
        },
        {
          "name": "Fine-tuned models",
          "cost": 0.0
        },
        {
          "name": "Embedding models",
          "cost": 0.0002
        },
        {
          "name": "Image models",
          "cost": 0.0
        },
        {
          "name": "Audio models",
          "cost": 0.0
        }
      ]
    },
    {
      "timestamp": 1685145600.0,
      "line_items": [
        {
          "name": "Instruct models",
          "cost": 0.0
        },
        {
          "name": "Chat models",
          "cost": 0.0144
        },
        {
          "name": "GPT-4",
          "cost": 0.0
        },
        {
          "name": "Fine-tuned models",
          "cost": 0.0
        },
        {
          "name": "Embedding models",
          "cost": 0.0
        },
        {
          "name": "Image models",
          "cost": 0.0
        },
        {
          "name": "Audio models",
          "cost": 0.0
        }
      ]
    }
  ],
  "total_usage": 649.65
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The response has a daily_costs object where each object contains a timestamp for the day and then the individual cost for each model like:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Instruct models&lt;/li&gt;
&lt;li&gt;Chat models&lt;/li&gt;
&lt;li&gt;GPT-4&lt;/li&gt;
&lt;li&gt;Fine-tuned models&lt;/li&gt;
&lt;li&gt;Embedding models&lt;/li&gt;
&lt;li&gt;Image models&lt;/li&gt;
&lt;li&gt;Audio models.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You can use this API call to get the total cost for any period. If you want the cost from the start, you can keep start_date as 2020-06-11 (the date when GPT-3 was launched)&lt;/p&gt;

&lt;p&gt;The best part here is that this API call works at the token level, so you can use this to calculate cost at a token level. You can create multiple tokens for various apps or clients and then maintain a limit at your end for each token. This is something that is totally missing in OpenAI’s current platform.&lt;/p&gt;

&lt;h2&gt;
  
  
  Python code to get cost details
&lt;/h2&gt;

&lt;p&gt;Here is a Python function that you can call from your app. You just need to pass the OPENAI_API_KEY, start_date and end_date. ORG_ID is optional.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;def get_openai_cost_details(api_key, start_date, end_date, org_id=None):
    '''
    Get cost details from OpenAI.
    start_date and end_date format: 2023-05-28
    '''
    headers = {
        'authority': 'api.openai.com',
        'accept': '*/*',
        'accept-language': 'en-GB,en-US;q=0.9,en;q=0.8',
        'authorization': f'Bearer {api_key}',
        'origin': 'https://platform.openai.com',
        'referer': 'https://platform.openai.com/',
        'sec-fetch-dest': 'empty',
        'sec-fetch-mode': 'cors',
        'sec-fetch-site': 'same-site',
        'sec-gpc': '1',
        'user-agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/103.0.5060.53 Safari/537.36',
    }
    if org_id:
        headers['openai-organization'] = f'org-{org_id}'
    params = {
        'end_date': end_date,
        'start_date': start_date
    }
    response = requests.get(
      'https://api.openai.com/dashboard/billing/usage',
        params=params,
        headers=headers
    )
    return response.json()

OPENAI_API_KEY = ''     # set the one for which you want to calculate cost

print(get_openai_cost_details(OPENAI_API_KEY, '2023-05-01', '2023-06-01'))
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  OpenAI Grant Details
&lt;/h2&gt;

&lt;p&gt;There is another API call to credit_grants the endpoint. It gives the detail of how many credits have been used. Here is the curl call&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;curl 'https://api.openai.com/dashboard/billing/credit_grants&amp;gt;'
  -H 'authority: api.openai.com'
  -H 'accept: */*'
  -H 'accept-language: en-GB,en-US;q=0.9,en;q=0.8'
  -H 'authorization: Bearer ${OPENAI_SESSION_KEY}'
  -H 'openai-organization: org-${ORG_ID}'
  -H 'origin: https://platform.openai.com'
  -H 'referer: https://platform.openai.com/'
  -H 'sec-fetch-dest: empty'
  -H 'sec-fetch-mode: cors'
  -H 'sec-fetch-site: same-site'
  -H 'sec-gpc: 1'
  -H 'user-agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/103.0.5060.53 Safari/537.36'
  --compressed
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;and here is the response&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{
  "object": "credit_summary",
  "total_granted": 100.0,
  "total_used": 100,
  "total_available": 0,
  "grants": {
    "object": "list",
    "data": [
      {
        "object": "credit_grant",
        "id": "",
        "grant_amount": 100.0,
        "used_amount": 100.0,
        "effective_at": 1685249945.0,
        "expires_at": 1685249945.0
      }
    ]
  }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Note that this API call can be made using session_key only. I tried the above curl  OPENAI_API_KEY and got the following error:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{'error': {'message': 'Your request to GET /dashboard/billing/credit_grants must be made with a session key (that is, it can only be made from the browser). You made it with the following key type: secret.', 'type': 'server_error', 'param': None, 'code': None}}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you really want to get the credit details, you will have to first get the session key using a login call.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Few things to note&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;The endpoints listed above are internal endpoints and may change from time to time. I remember the subscription and  billing endpoint use to take OPENAI_API_KEY some time back. Now it’s taking OPENAI_SESSION_KEY . So they can be changed or removed without any prior notice. &lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Please note that the content, findings, interpretations, and conclusions expressed in this document are solely my own and do not reflect the views or policies of OpenAI. This document is intended for informational purposes only and should not be considered as an official statement or endorsement from OpenAI.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Follow me on &lt;a href="https://twitter.com/taranjeetio"&gt;Twitter&lt;/a&gt;&lt;/p&gt;

</description>
      <category>python</category>
      <category>programming</category>
      <category>openai</category>
      <category>ai</category>
    </item>
    <item>
      <title>Learning Path for a Computer Science Student</title>
      <dc:creator>Taranjeet Singh</dc:creator>
      <pubDate>Sat, 08 Jun 2019 13:35:30 +0000</pubDate>
      <link>https://forem.com/taranjeet/learning-path-for-a-computer-science-student-1da2</link>
      <guid>https://forem.com/taranjeet/learning-path-for-a-computer-science-student-1da2</guid>
      <description>&lt;p&gt;&lt;em&gt;Note: this is cross-posted from &lt;a href="https://taranjeet.cc/learning-path-for-a-computer-science-student/"&gt;my blog&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;This article is written for a student pursuing graduation in computer science for a duration of 4 years. It provides a learning path for a good career in software development, which can be either of the following:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Working for an early stage or high growth startup&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Working for a big MNC&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This article assumes the following:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;There can be many achievements for a computer science student, but it mainly focuses on a good career in software development.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;A good career in software development means either working for an early stage or a high growth startup or working for a big MNC.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;This article does not suggest to skip the college syllabus. In fact, it suggests how to put efforts alongside your college curriculum.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;This article does not provide a learning path for any specialized roles like Machine Learning Engineer, or Data Scientist. However, it tries providing an optimum path for students interested in Software engineering roles.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;This article is written keeping in mind Computer Science graduates, but it is not strict about it. It can be used by anyone interested in pursuing a career in software development.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;It does not provide any suggestion for "resources". You can find the resources by doing a Google search for "how to learn X".&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Last, this article is just a suggestion of how should you structure yourself, so that you are more aware of how to plan and put your efforts during your graduation course.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In most of the tech companies, an entry-level software engineer role expects the following:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Good knowledge of algorithms and data structures, so that you can pick up things on the fly and are able to comprehend most of the logical things.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;An understanding and knowledge of the language and frameworks used in the company. It's better if you know.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Before we begin, let's understand how is the graduation course generally structured. A 4-year programme is divided into 8 semesters, with a break of 1–3 months in between. Most of the courses run the class(around 8 hours) 6 days a week, with some half days(4 hours). This also includes the practical implementation part a.k.a Labs related to the courses you take.&lt;/p&gt;

&lt;p&gt;Let's first plan out the time of how and when can you spend your time in following this learning path. We will call this as your additional time. You can spend your time in the following way:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;2 hours per day, on weekdays(Monday - Friday)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;4 hours per day, on weekends(Saturday, Sunday)&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For each day, you can split it into shifts, like morning and evening.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;1 hour per day, on weekdays, in Morning&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;1 hour per day, on weekdays, in Evening&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;2 hours per day, on weekends, in Morning&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;2 hours per day, on weekends, in Evening.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;However, this is just an outline and you are free to structure your day/week in whatever way you want to.&lt;/p&gt;

&lt;p&gt;Also, for better understanding and be clear with your goals, the semesters are divided into the following categories&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Maker&lt;/strong&gt; - This means that the entire focus is on building a product. The product can be as simple as a static web page to as complex as a chat application. It is also important to understand that here learning will be there, but it will be mainly driven towards building a product.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Learner&lt;/strong&gt; - This means that the entire focus is on learning new stuff. It will focus less on creating products and more on building your base and depth about concepts.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Exposure&lt;/strong&gt; - This will consist mainly of interactions with the community and learning/making by observing others or collaborating with others.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Let's us begin of how you should put additional efforts alongside your semester syllabus to have a better career in software development.&lt;/p&gt;

&lt;h3&gt;
  
  
  First Semester [Maker]
&lt;/h3&gt;

&lt;p&gt;In this semester, learn HTML, CSS. You should learn how to build static web pages. If you are running out of ideas, of what to build, then start creating static pages of popular and frequently used websites, like Twitter, GitHub, Medium, HackerNews.&lt;/p&gt;

&lt;p&gt;Next, you should learn how to use git and GitHub. It's very important and will be used throughout your entire career in software development. Also, commit all the code that you create during this semester to your GitHub account.&lt;/p&gt;

&lt;p&gt;You can learn JavaScript and jQuery if you want to add some interactivity to the pages. This is additional.&lt;/p&gt;

&lt;p&gt;You can participate in international challenges like &lt;a href="https://www.100daysofcode.com/"&gt;100 days of code&lt;/a&gt;. This gives you a community of like minded people willing to learn and helps you in case you get stuck.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Time:&lt;/strong&gt; Spend all of your additional time in learning frontend development.&lt;/p&gt;

&lt;h3&gt;
  
  
  Second Semester[Maker]
&lt;/h3&gt;

&lt;p&gt;In this semester, learn Backend development. You can pick up any language like Javascript, Php, Python, Ruby or Java. The choice of framework is also yours. But the main focus here should be able to create fully functional dynamic apps.&lt;/p&gt;

&lt;p&gt;At this point, you can also start participating in hackathons with your fellow peers and learn more about building products in a time restricted manner and working in a team.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Time:&lt;/strong&gt; Spend all of your additional time in learning backend development&lt;/p&gt;

&lt;h3&gt;
  
  
  Third Semester[Learner + Maker]
&lt;/h3&gt;

&lt;p&gt;This semester, you should focus on learning data structures and algorithms. You can try competitive programming to learn. It gives you an environment and a community. Or you can learn algorithms by reading and implementing them.&lt;/p&gt;

&lt;p&gt;Apart from this, also focus on your web development projects. Keep participating in hackathons and building side projects on ideas that fascinate you.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Time:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Spend 30% of your time in making products(3 hours per day on weekends).&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Spend 70% of your time in algorithms(rest of the additional time).&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Fourth Semester[Exposure]
&lt;/h3&gt;

&lt;p&gt;This semester, start contributing to open source projects on framework/language of your own choice. You can use &lt;a href="https://github.com/trending"&gt;GitHub trending projects&lt;/a&gt; or &lt;a href="https://summerofcode.withgoogle.com/archive/"&gt;GSOC past projects&lt;/a&gt; to select a project.&lt;/p&gt;

&lt;p&gt;Also, you should participate in GSoC. At least, spend some time with an organization, understand their codebase and fix some issues. If you are excited by what they are solving, then write a GSOC proposal on a project idea. This will help you learn more about how to plan out the development, how to manage yourself, and how to be an efficient communicator.&lt;/p&gt;

&lt;p&gt;Apart from this, keep sharpening your algorithm skills.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Time:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Spend 30% of your time in algorithms(1 hour a day, for 6 days a week)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Spend 70% of your time in open source(rest of the additional time)&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Fifth Semester [Learner + Maker]
&lt;/h3&gt;

&lt;p&gt;This semester, you need to balance and focus on most of the things you have done until now. Spend some of your time(30%) in refining your algorithm skills. Spend 40% of your time in going deep in maker skills(frontend or backend development or both).&lt;/p&gt;

&lt;p&gt;Spend the last 30% in open source stuff. For open source, you can contribute to some projects or publish packages of your own. In fact, one suggestion for this can be to create an open source repo of algorithms in a language of your own choice. This will deepen your knowledge of algorithms, help you learn more about the caveats of a language and help understand how to collaborate and maintain an open source project.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Time:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Spend 30% of your time in algorithms(1 hour a day, for 6 days a week, in the morning).&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Spend 30% of your time in open source(1 hour a day, for 6 days a week, in the evening).&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Spend the remaining 40% of your time in maker stuff(rest of the additional time).&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Sixth Semester[Learner + Maker]
&lt;/h3&gt;

&lt;p&gt;This semester, spend time(50%) in preparing and practising algorithms, the next 40% time in building stuff. In fact, this time can be used to apply to GSoC as well.&lt;/p&gt;

&lt;p&gt;At last, spend remaining 10% of your time in learning essentials skills for an interview like resume and portfolio building, and preparing for managerial round interview questions.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Time:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Spend 50% of your time in algorithms(1 hour per day on weekdays, 2 hours per day on weekends).&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Spend 40% of your time in building stuff(1 hour per day on weekdays, 2 hours on Saturday).&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Spend 10% of your time in essential skills(2 hours on Sunday).&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Seventh Semester[Exposure]
&lt;/h3&gt;

&lt;p&gt;This semester, spend entire of your time in preparing for job interviews and securing a job. This will require you to visit each of the skills learned above and balance them.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Time:&lt;/strong&gt; Spend all of your additional time in this.&lt;/p&gt;

&lt;h3&gt;
  
  
  Eight Semester[Exposure]
&lt;/h3&gt;

&lt;p&gt;This semester, spend time preparing for interviews if not placed. If you are placed, then you should spend time expanding your overall knowledge about software development. You should learn &lt;a href="https://en.wikipedia.org/wiki/Test-driven_development"&gt;Test Driven Development&lt;/a&gt; and &lt;a href="https://en.wikipedia.org/wiki/Object-oriented_programming"&gt;Object-Oriented Programming&lt;/a&gt;(if not learned until now). You should learn Deploying applications on cloud providers like AWS or Heroku. You should participate in local meetups of your preferred language and should give a talk. You can also use this time to explore other frontend frameworks like React, Vue or Angular.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Time:&lt;/strong&gt; Spend all of your additional time in this.&lt;/p&gt;

&lt;h3&gt;
  
  
  Conclusion
&lt;/h3&gt;

&lt;p&gt;This article suggests a leaning path for a career in software development, which can be followed alongside the curriculum of the college. It is written keeping in mind the experiences and observations of the author, in solving the industry-academia gap.&lt;/p&gt;




&lt;p&gt;Special Thanks to &lt;a href="https://github.com/aviaryan"&gt;Avi Aryan&lt;/a&gt; and &lt;a href="https://github.com/PragatiVerma18"&gt;Pragati Verma&lt;/a&gt; for reading draft of this article and suggesting improvements.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>career</category>
      <category>beginners</category>
      <category>cs</category>
    </item>
    <item>
      <title>Class-Based Views in Django</title>
      <dc:creator>Taranjeet Singh</dc:creator>
      <pubDate>Sat, 25 May 2019 11:39:27 +0000</pubDate>
      <link>https://forem.com/taranjeet/class-based-views-in-django-3bc</link>
      <guid>https://forem.com/taranjeet/class-based-views-in-django-3bc</guid>
      <description>&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--6YS4d_3f--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/960/1%2Ame2JPfmwjMGoLxwTggGKlA.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--6YS4d_3f--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/960/1%2Ame2JPfmwjMGoLxwTggGKlA.png" alt=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Creating CRUD interfaces with DRY patterns in Django.&lt;/p&gt;

&lt;p&gt;In this tutorial, we will learn how to use Class-based views to create CRUD interfaces for any model.&lt;/p&gt;

&lt;p&gt;At the end of this tutorial, we will be able to add, list, view, update and delete a particular model(here &lt;code&gt;Book&lt;/code&gt; and the project is based on &lt;code&gt;library&lt;/code&gt;). This tutorial can be extended to create CRUD based interfaces for any model.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--eGGq-Ce_--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://cdn-images-1.medium.com/max/1600/1%2ASXu4Y9dYW5WQsBjbCg6LcQ.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--eGGq-Ce_--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://cdn-images-1.medium.com/max/1600/1%2ASXu4Y9dYW5WQsBjbCg6LcQ.gif" alt=""&gt;&lt;/a&gt;Gif showcasing CRUD interfaces for Book model&lt;br&gt;
&lt;/p&gt;

&lt;p&gt;Django has 5 &lt;a href="https://docs.djangoproject.com/en/2.2/topics/class-based-views/"&gt;class based views&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;ListView — to view the list of objects&lt;/li&gt;
&lt;li&gt;CreateView — to create a particular object&lt;/li&gt;
&lt;li&gt;DetailView — to view a particular object&lt;/li&gt;
&lt;li&gt;UpdateView — to update a particular object&lt;/li&gt;
&lt;li&gt;DeleteView — to delete a particular object&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Let’s consider that we are creating a library project. We will have a Book model in an app called &lt;code&gt;store&lt;/code&gt;.&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;We will create a Django application, which will use Class based Views to create CRUD interfaces around the &lt;code&gt;Book&lt;/code&gt; model. The complete code for the repo can be found &lt;a href="https://github.com/all-about-django/django-cbv-tutorial"&gt;here&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We will start by creating a list of all the &lt;code&gt;Book&lt;/code&gt; objects stored in the database.&lt;/p&gt;

&lt;h4&gt;
  
  
  ListView
&lt;/h4&gt;

&lt;p&gt;We will use &lt;code&gt;ListView&lt;/code&gt; to create a new view called &lt;code&gt;BookListView&lt;/code&gt; which will also have support for pagination.&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;In the above code, we can see that we are overriding &lt;code&gt;get_context_data&lt;/code&gt; to add support for pagination, by passing in an additional query parameter called page.&lt;/p&gt;

&lt;p&gt;We will add a URL for BookListView in &lt;code&gt;store/urls.py&lt;/code&gt;&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;We need to make sure that the &lt;code&gt;store&lt;/code&gt; app URLs are included in the project URLs. If not, we will have to include it.&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;We will now write the template code. We are using Bootstrap v4 and have a base template called &lt;code&gt;__base.html&lt;/code&gt; from which we extend every other template. The &lt;code&gt;__base.html&lt;/code&gt; looks like&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;We will now create &lt;code&gt;book/list.html&lt;/code&gt; .&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;We can see that &lt;code&gt;book/list.html&lt;/code&gt; includes a generic template &lt;code&gt;_pagination.html&lt;/code&gt; .&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;With this, our first Class-Based View &lt;code&gt;ListView&lt;/code&gt; is complete.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Ybzr_K13--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/1024/1%2AdvRmXZJ-FE0QvMs3_IOThA.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Ybzr_K13--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/1024/1%2AdvRmXZJ-FE0QvMs3_IOThA.png" alt=""&gt;&lt;/a&gt;Listview for Books&lt;/p&gt;

&lt;h4&gt;
  
  
  CreateView
&lt;/h4&gt;

&lt;p&gt;Next, we will add support for creating a new &lt;code&gt;Book&lt;/code&gt; by using &lt;code&gt;CreateView&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;We will add the following code in the views.&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;We will add a new URL for the above view.&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;We will now add a template &lt;code&gt;book/create.html&lt;/code&gt; .&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;This completes our second Class based View &lt;code&gt;CreateView&lt;/code&gt;. We can see that Class-Based Views helps us achieve &lt;code&gt;DRY&lt;/code&gt; code pattern.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--TXVBIQem--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/1024/1%2A6FpjJTKr8fJzsXR59ZlzsQ.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--TXVBIQem--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/1024/1%2A6FpjJTKr8fJzsXR59ZlzsQ.png" alt=""&gt;&lt;/a&gt;CreateView for Book&lt;/p&gt;

&lt;h4&gt;
  
  
  DetailView
&lt;/h4&gt;

&lt;p&gt;We will now create a detail view using &lt;code&gt;DetailView&lt;/code&gt; class-based view. We will add the following code in views.&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;We will add the following URL to include the above detail view.&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;We will now add a new template &lt;code&gt;book/detail.html&lt;/code&gt;&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;This completes our detail view.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--6-YwZw-f--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/1024/1%2Ad0fV3lpCtULXiBOGKuMcJw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--6-YwZw-f--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/1024/1%2Ad0fV3lpCtULXiBOGKuMcJw.png" alt=""&gt;&lt;/a&gt;DetailView for Book&lt;/p&gt;

&lt;h4&gt;
  
  
  UpdateView
&lt;/h4&gt;

&lt;p&gt;Now we will add a &lt;code&gt;UpdateView&lt;/code&gt; for this.&lt;/p&gt;

&lt;p&gt;We will add the following code in the views.&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;We will add a URL for this view.&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;We will add &lt;code&gt;book/update.html&lt;/code&gt; template.&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;This completes our &lt;code&gt;UpdateView&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--4uhgLVpE--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/1024/1%2AES9Fer5L2nR5XcaiT9OMWg.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--4uhgLVpE--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/1024/1%2AES9Fer5L2nR5XcaiT9OMWg.png" alt=""&gt;&lt;/a&gt;UpdateView for Book&lt;/p&gt;

&lt;h4&gt;
  
  
  DeleteView
&lt;/h4&gt;

&lt;p&gt;We will now add the last &lt;code&gt;DeleteView&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;We will add the following code in the views.&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;We will add a URL for the above view&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;We will add a template &lt;code&gt;book/delete.html&lt;/code&gt; for the above view&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;This completes our &lt;code&gt;DeleteView&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--1rtgSTKs--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/1024/1%2AL8cRbPYpaWlUC4USkF1hZQ.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--1rtgSTKs--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/1024/1%2AL8cRbPYpaWlUC4USkF1hZQ.png" alt=""&gt;&lt;/a&gt;DeleteView for Book&lt;/p&gt;

&lt;h4&gt;
  
  
  Conclusion
&lt;/h4&gt;

&lt;p&gt;In this tutorial, we learned how to use Django Class-Based Views to create CRUD interfaces for a given model. This tutorial touched on the basics and focussed very little on styling and extensibility.&lt;/p&gt;

&lt;h4&gt;
  
  
  Extensions
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;A &lt;a href="https://github.com/all-about-django/django-cbv-using-argon-theme"&gt;new repo&lt;/a&gt; is created using this tutorial and &lt;a href="https://www.creative-tim.com/product/argon-design-system"&gt;Argon Design System&lt;/a&gt;. Most of the code changes are in templates only.&lt;/li&gt;
&lt;/ul&gt;




</description>
      <category>webdev</category>
      <category>python</category>
      <category>django</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Django Migrations: A Primer</title>
      <dc:creator>Taranjeet Singh</dc:creator>
      <pubDate>Mon, 12 Feb 2018 18:28:34 +0000</pubDate>
      <link>https://forem.com/taranjeet/django-migrations-a-primer-163b</link>
      <guid>https://forem.com/taranjeet/django-migrations-a-primer-163b</guid>
      <description>&lt;p&gt;Migrations in Django are a way of propagating changes made in the model into the database schema.&lt;/p&gt;

&lt;p&gt;Official &lt;a href="https://docs.djangoproject.com/en/2.0/topics/migrations/"&gt;Django docs&lt;/a&gt; summarizes Migration as&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Migrations are Django’s way of propagating changes you make to your models (adding a field, deleting a model, etc.) into your database schema&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Migrations were introduced as a &lt;a href="https://docs.djangoproject.com/en/2.0/releases/1.7/#schema-migrations"&gt;built-in functionality&lt;/a&gt; in Django version 1.7. Migrations are created manually by running certain commands but they automate the task of applying changes to the database. We can create a migration for any changes in the model, like&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Add a new model.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Add a particular field in a model&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Update field attributes in a model&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Delete a field in a model&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Rename a field in a model&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Automatically generated migrations are used to apply changes to the database schema. To apply changes in data, one needs to manually write the &lt;a href="https://docs.djangoproject.com/en/2.0/topics/migrations/#data-migrations"&gt;data migrations&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Let’s consider an example of a Library system and walk through the whole example of creating and applying migrations. Consider that our project is &lt;code&gt;django_library&lt;/code&gt; and there is an app called &lt;code&gt;library&lt;/code&gt; in it. We are right now focussing only on the &lt;code&gt;Book&lt;/code&gt; model.&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;h4&gt;
  
  
  How to create migrations
&lt;/h4&gt;

&lt;p&gt;To create the migration, Django provides a management command called &lt;code&gt;makemigrations&lt;/code&gt;. It can be run as&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;Here &lt;code&gt;name_of_app&lt;/code&gt; is optional.&lt;/p&gt;

&lt;p&gt;Consider our library app, migrations for it can be created by running the following command.&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;A file called &lt;code&gt;0001_initial.py&lt;/code&gt; will be created in &lt;code&gt;library/migrations&lt;/code&gt; directory.&lt;/p&gt;

&lt;p&gt;Since this is the first migration for an app, Django names it as &lt;code&gt;initial&lt;/code&gt; migration.&lt;/p&gt;

&lt;p&gt;For every other migration, Django names it using date and time like &lt;code&gt;0007_auto-20180208_1613.py&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;It is always advised to create a named migration since it becomes easier to infer just by looking at the name of what the migration is about.&lt;/p&gt;

&lt;p&gt;We can create named migration by running the following command&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;A small note here, the name of migration should not exceed 50 characters.&lt;/p&gt;

&lt;p&gt;Let’s add another field named &lt;code&gt;publisher&lt;/code&gt; in our &lt;code&gt;Book&lt;/code&gt; model. Our model will now look like&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;To create named migration for this, we need to run &lt;code&gt;makemigrations&lt;/code&gt; as&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;h4&gt;
  
  
  How to apply a migrations
&lt;/h4&gt;

&lt;p&gt;Once the migration, we need to apply them, so that changes can be reflected in the databases as well.&lt;/p&gt;

&lt;p&gt;To apply migration, we need to run &lt;code&gt;migrate&lt;/code&gt; command as follows&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;By running this command, all the unapplied migration are applied to the database.&lt;/p&gt;

&lt;p&gt;In our case for &lt;code&gt;Book&lt;/code&gt; model, both &lt;code&gt;0001_initial&lt;/code&gt; and &lt;code&gt;0002_add_publisher&lt;/code&gt; will be applied.&lt;/p&gt;

&lt;p&gt;Whenever any migration is applied, Django records this in a table called &lt;code&gt;django_migrations&lt;/code&gt;.&lt;/p&gt;

&lt;h4&gt;
  
  
  How to undo migrations
&lt;/h4&gt;

&lt;p&gt;Many times, we need to undo the migration as well. Or in simple words, we need to undo the changes which are done to the database schema by undoing that migration&lt;/p&gt;

&lt;p&gt;Undoing a migration can be done by using &lt;code&gt;migrate&lt;/code&gt; command, but we need to pass app name as well as the name of migration at which we want to rollback.&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;Let’s remove &lt;code&gt;publisher&lt;/code&gt; field from our &lt;code&gt;Book&lt;/code&gt; model. This can be done by undoing &lt;code&gt;0002_add_publisher&lt;/code&gt; migration.&lt;br&gt;&lt;br&gt;
Since we need to undo this migration, we will pass &lt;code&gt;0001_initial&lt;/code&gt; as an argument to migrate. The command will look like&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;By running this command, &lt;code&gt;publisher&lt;/code&gt; field will be removed from &lt;code&gt;book&lt;/code&gt; table in the database schema.&lt;/p&gt;

&lt;h4&gt;
  
  
  How to fake migrations
&lt;/h4&gt;

&lt;p&gt;Many times, We just need to run the migration without actually changing the database schema. It happens when we have taken a dump from our staging or production database.&lt;br&gt;&lt;br&gt;
To deal with such cases, Django provides a way called &lt;strong&gt;fake migration&lt;/strong&gt; , which applies the migration but does not affect the database schema.&lt;/p&gt;

&lt;p&gt;In simpler words, for each migration which is faked, an entry is created in &lt;code&gt;django_migrations&lt;/code&gt; table but no changes are done in the database schema.&lt;/p&gt;
&lt;h4&gt;
  
  
  How to clear migration for an app
&lt;/h4&gt;

&lt;p&gt;Django provides with a built-in functionality, which allows us to clear all the migration for a particular app. To clear migration for an app, we need to run the following command&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;Here zero tells migrate command to clear the migration for a particular app.&lt;/p&gt;

&lt;h4&gt;
  
  
  How to list all migrations
&lt;/h4&gt;

&lt;p&gt;Django provides a built-in command called &lt;code&gt;showmigrations&lt;/code&gt; to list and display all migrations. It can be run as&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;When this command is run for our &lt;code&gt;django_library&lt;/code&gt; application, we get the following output.&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;Here &lt;code&gt;[X]&lt;/code&gt; indicates that the migration has been applied and &lt;code&gt;[]&lt;/code&gt; indicates that the migration has not been applied. In our case, &lt;code&gt;0002_add_publisher&lt;/code&gt; is not applied.&lt;/p&gt;

&lt;h4&gt;
  
  
  Workflow
&lt;/h4&gt;

&lt;p&gt;The basic process of creating and applying migration is the following.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;First, make changes to the model. This can be adding a new model or updating/removing any fields from the model.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Create migrations by running &lt;code&gt;makemigrations&lt;/code&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Apply migration by running &lt;code&gt;migrate&lt;/code&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  Role of django_migrations table
&lt;/h4&gt;

&lt;p&gt;Whenever any migration is applied or unapplied, Django records this in a table called &lt;code&gt;django_migrations&lt;/code&gt;. This table stores the name of the app, name and of migration and a datetime field, which tells when the migration was applied.&lt;/p&gt;

&lt;p&gt;This table is used by Django to keep track of which migration to apply and which migrations have been applied.&lt;/p&gt;

&lt;p&gt;A very likely doubt that arises is no changes are reflected in the database schema when a migration file is manually changed and applied again. This happens because Django has already created an entry for it in &lt;code&gt;django_migrations&lt;/code&gt; table. One way to deal with this situation can be deleting the concerned row and then running the migrations again(this is not the official way, but it works).&lt;/p&gt;

&lt;h4&gt;
  
  
  What is a migration file?
&lt;/h4&gt;

&lt;p&gt;Whenever &lt;code&gt;makemigrations&lt;/code&gt; is run, a python file is created. This file contains a class called &lt;code&gt;Migration&lt;/code&gt; which contains two attributes&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;dependencies&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is an important attribute, which tells that this migration depends on the previous one. It actually allows the migration to be applied in a linear fashion.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;operations&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is an array, which contains all the changes that need to be applied to the database schema.&lt;/p&gt;

&lt;p&gt;One important thing to note here is that this migration file needs to be added to the Version Control System(git) as well.&lt;/p&gt;

&lt;h4&gt;
  
  
  Practical Use Cases
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Migrations gives us the flexibility of propagating database related changes easily across a team. Migration created by a developer can be added to VCS, which later can be applied by another dev, simply by running the &lt;code&gt;migrate&lt;/code&gt; command.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Another way of using migration can be to apply changes in the development environment and fake migrations on the production environment. This is especially helpful in a large organization, where the database is managed specifically by Database Administrators.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  Advantages
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Migration gives us the flexibility to track database changes in a version control system. This way it becomes easier for us to see how the database changed over a period of time.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Migration offloads the process of manually applying a set of changes to the database. More than one migration can be applied at once by simply running the &lt;code&gt;migrate&lt;/code&gt; command.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  Summary
&lt;/h4&gt;

&lt;p&gt;Django provides a built-in functionality of propagating changes made in the model to the database schema. Migrations first need to be created using &lt;code&gt;makemigrations&lt;/code&gt;. Once created, migrations can be applied by running &lt;code&gt;migrate&lt;/code&gt; command. Creating a migration and applying a migration both are two different processes. Automatically generated migrations are used to apply changes only to the database schema. Migrations for data needs to be manually written and applied.&lt;/p&gt;




</description>
      <category>database</category>
      <category>python</category>
      <category>django</category>
      <category>webdev</category>
    </item>
  </channel>
</rss>
