I am having issue with htmx and django

I am trying to create a social media add friend system with django and htmx. I keep getting 403 Forbbiden which is associated with invalid csrf token. I have tried to fix it but to no avail.

views.py


def get_friends(current_user):

    friend_ids = Friend.objects.filter(
        Q(user=current_user, request_accepted=True) |
        Q(requests_user=current_user, request_accepted=True)
    ).values_list('user', 'requests_user')  # id of friends of current user in a list

    friend_ids = set(
        [user_id for sublist in friend_ids for user_id in sublist if user_id != current_user.id])

    # user model of friends of current user
    friends = User.objects.filter(id__in=friend_ids)

    return friends


def get_users_no_rel(current_user):
    sent_requests = Friend.objects.filter(
        user=current_user)

    list_sent_requests = sent_requests.values_list('requests_user', flat=True)

    received_requests = Friend.objects.filter(
        requests_user=current_user)
    list_received_requests = received_requests.values_list('user', flat=True)

    excluded_users = list(list_sent_requests) + \
        list(list_received_requests) + \
        [current_user.id]  # list of users that have no relationship with the current user

    # user model of user in the excluded_users
    users = User.objects.exclude(id__in=excluded_users)

    return users


def friends_view(request, **kwargs):
    current_user = request.user

    sent_requests = Friend.objects.filter(
        user=current_user)

    list_sent_requests = sent_requests.values_list('requests_user', flat=True)

    received_requests = Friend.objects.filter(
        requests_user=current_user)
    list_received_requests = received_requests.values_list('user', flat=True)

    excluded_users = list(list_sent_requests) + \
        list(list_received_requests) + \
        [current_user.id]  # list of users that have no relationship with the current user

    # user model of user in the excluded_users
    users = User.objects.exclude(id__in=excluded_users)

    friends = get_friends(request.user)

    if request.htmx:
        users = get_users_no_rel(request.user)

    context = {
        'sent_requests': sent_requests,
        # 'received_requests': received_requests,
        'friends': friends,
        'users_list': users
    }

    return render(request, 'base/friends.html', context)


def friends(request, username):
    current_user = User.objects.get(username=username)
    friends = get_friends(current_user=current_user)

    if request.htmx:
        html = render_to_string(
            'base/partials/friends_lists.html',
            {
                'friends': friends
            },
            request=request
        )

        return HttpResponse(html)


def add_friend(request, username):
    # user that would be receiving friend request from the current user
    user_to_request = User.objects.get(username=username)

    # creating request
    send_friend_request = Friend.objects.create(
        user=request.user, requests_user=user_to_request)
    sent_requests = Friend.objects.filter(
        user=request.user, request_accepted=False)

    users = get_users_no_rel(request.user)

    add_friend_html = render_to_string(
        'base/partials/add_friends.html', {'user_list': users})
    sent_requests_html = render_to_string(
        "base/partials/sent_requests.html", {'sent_requests': sent_requests})

    return HttpResponse(sent_requests_html)


def cancel_request(request, request_id):
    request_ = get_object_or_404(Friend, id=request_id)

    request_.delete()

    sent_requests = Friend.objects.filter(
        user=request.user, request_accepted=False)
    sent_requests_html = render_to_string(
        "base/partials/sent_requests.html", {'sent_requests': sent_requests})
    response = HttpResponse(sent_requests_html)
    response['X-CSRFToken'] = get_token(request)
    return response

friends.html

{% extends "base.html" %} {% block content %}
<div>
  <input
    name="search"
    placeholder="Search for friends..."
    class="p-3 my-2 rounded-lg border-2 border-slate-300 w-full focus: outline-none focus:border-slate-500"
  />
</div>

<div class="my-3">
  <div class="flex flex-col gap-2 mb-5" id="add" hx-swap="outerHTML">
    {% for user in users_list %}
    <div
      class="p-3 flex justify-between items-center transition-colors border border-slate-300 rounded-lg"
    >
      <div>
        <p class="text-base font-medium">{{ user }}</p>
        <a
          href="{% url 'profile' user.username %}"
          class="text-slate-600 font-medium text-sm"
          >@{{ user.username }}</a
        >
      </div>
      <div>
        <button
          id="add-{{user.username}}"
          hx-post="{% url 'add_friend' user.username %}"
          hx-target="#sent-requests"
          hx-trigger="click"
          hx-swap="innerHTML"
          hx-headers='{"X-CSRFToken": "{{ csrf_token }}"}'
          class="p-3 bg-purple-600 hover:bg-purple-700 rounded-lg text-white font-bold"
        >
          Add friend
        </button>
      </div>
    </div>
    {% empty %}
    <p>No user found</p>
    {% endfor %}
  </div>

  <h2 class="text-3xl font-bold my-4">Sent Friend Requests</h2>
  <div class="flex flex-col gap-2 mb-5" id="sent-requests" hx-swap="innerHTML">
    {% include "base/partials/sent_requests.html" with sent_requests=sent_requests %}
  </div>

  <div class="flex justify-between items-center">
    <h2 class="text-3xl font-bold my-4">Friends</h2>
    <p class="font-bold text-lg text-purple-600">{{friends.count}}</p>
  </div>
  <div id="friends" hx-swap="innerHTML">
    {% include 'base/partials/friends_lists.html' %}
  </div>
</div>
{% endblock content %}

sent_requests.html


{% for request in sent_requests %}
<div
  class="p-3 flex justify-between items-center transition-colors border mb-2 border-slate-300 rounded-lg"
>
  <div>
    <p class="text-base font-medium">{{ request.requests_user.name }}</p>
    <a class="text-slate-600 font-medium text-sm"
      >@{{ request.requests_user.username }}</a
    >
  </div>
  <div id="friend-{{ request.username }}">
    <button
      id="cancel-{{user.username}}"
      hx-post="{% url 'cancel_request' request.pk %}"
      hx-trigger="click"
      hx-target="#sent-requests"
      hx-swap="innerHTML"
      hx-headers='{"X-CSRFToken": "{{ csrf_token }}"}'
      class="p-3 bg-red-100 hover:bg-red-200 rounded-lg text-red-800 font-bold flex gap-2 items-center"
    >
      <i class="fa-solid fa-xmark text-lg"></i> Cancel Request
    </button>
  </div>
</div>

{% empty %}
<p>No sent requests.</p>
{% endfor %}

The code is supposed to cancel the request on click and then update the sent requests ui, and remove the users after they are added as friends then update the users ui

When you use htmx with django for POST request or any request you need to add hx-headers to body tag like below.

<body hx-headers='{"X-CSRF-TOKEN": "{{ csrf_token() }}"}'>

This may be solution for your 403 Forbbiden error.

For more info you can refer :- Make htmx Pass the CSRF Token

Back to Top