Значения JSONField Django не возвращаются в правильном формате JSON в шаблоне при извлечении с помощью javascript

У меня есть такая модель, как


class UserGroup(models.Model):
    owner = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE, unique=False, related_name="group_owner")
    name = models.CharField(max_length=128)
    group_users = models.JSONField(models.EmailField(), default=list, blank=True)

    def get_absolute_url(self):
        return reverse("user_group_instance", kwargs={"pk": self.pk})

теперь в моем другом приложении я отфильтровываю список UserGroup экземпляров для данного пользователя и анализирую этот запрос, присваивая ему значение ModelMultipleChoiceField.

Это работает нормально и тому подобное, проблема в том, что я хочу извлечь group_users из шаблона и добавить их в массив, используя java-script, таким образом, я должен создать div, который хранит (Я бы предположил) json-массив в атрибуте data-members

<div id="group-modal">
        {% for group in form.groups.field.queryset %}
            <label>
                <input type="checkbox" class="group-checkbox" value="{{ group.id }}" data-members="{{ group.group_users|safe }}">
                {{ group.name }}
            </label>
            <br>
        {% endfor %}

</div>

теперь, когда я в javascript получаю данные из data-members, возвращаемая строка не является json-объектом, например, это "['hello@world', 'foo@bar']".

Javascript является (упрощенным)

document.getElementById("save-groups").addEventListener("click", function() {
            let selectedPeople = [];
            document.querySelectorAll(".group-checkbox:checked").forEach(checkbox => {
                let members = JSON.parse(checkbox.getAttribute("data-members"));
                selectedPeople = [...new Set([...selectedPeople, ...members])]; 
            });

и JSON.parse завершается ошибкой. Я просто не могу понять, почему; я не выполняю никакой ручной сериализации json-данных, поэтому я позволяю django делать это.

Я мог бы обойти это, выполнив некоторую замену регулярных выражений, но я предпочел бы выяснить, в чем проблема

Я не уверен, что вам нужно передавать EmailField в качестве кодера для JSONField. Если вы хотите убедиться, что значение из этого поля в шаблоне отображается как JSON, а не как python dict, вы можете создать пользовательский декодер:

import json

class CustomDecoder(json.JSONDecoder):
    def decode(self, obj):
        return json.dumps(super().decode(obj))
    

class Order(models.Model):
    sku_id = models.CharField(max_length=10)
    group_users = models.JSONField(decoder=CustomDecoder, blank=True)

Тогда сохранение {"email_address": "foo@bar.com"}" в конечном итоге преобразуется в JSON в вашем шаблоне, который может быть передан в JSON.parse()

Я не уверен, но я думаю, что ваша проблема связана с тем, как движок шаблонов Django отображает данные поля JSON. Когда вы используете {{ group.group_users|safe }}, Django преобразует данные JSON в строковое представление, но это не гарантирует, что строка находится в допустимом формате JSON, который JavaScript может анализировать напрямую.

В вашем случае поле group_users отображается в виде строки списка python (например, "['hello@world', 'foo@bar']"), которая не является допустимым json. в json требуются двойные кавычки для строк, и вся структура должна быть заключена в двойные кавычки. чтобы решить эту проблему, вам нужно убедиться, что данные отображаются в шаблоне как допустимая строка json. Вы можете добиться этого, используя фильтр шаблонов json_script в Django, который безопасно выводит объект Python в виде сценария в кодировке JSON.

Я думаю, вы можете изменить свой шаблон следующим образом:

<div id="group-modal">
{% for group in form.groups.field.queryset %}
    <label>
        <input type="checkbox" class="group-checkbox" value="{{ group.id }}" data-members="{{ group.group_users|json_script:'group-members' }}">
        {{ group.name }}
    </label>
    <br>
{% endfor %}
</div>

фильтр json_script выведет тег с данными json, к которым вы затем сможете получить доступ в своем JavaScript, и, в качестве альтернативы, если вы хотите продолжать использовать атрибут data-members, вы можете вручную сериализовать данные json в шаблоне:

<div id="group-modal">
{% for group in form.groups.field.queryset %}
    <label>
        <input type="checkbox" class="group-checkbox" value="{{ group.id }}" data-members="{{ group.group_users|json_script:'group-members' }}">
        {{ group.name }}
    </label>
    <br>
{% endfor %}

И затем в вашем javascript вы можете правильно проанализировать данные json:

document.getElementById("save-groups").addEventListener("click", function() {
let selectedPeople = [];
document.querySelectorAll(".group-checkbox:checked").forEach(checkbox => {
    let members = JSON.parse(checkbox.getAttribute("data-members"));
    selectedPeople = [...new Set([...selectedPeople, ...members])]; 
});
console.log(selectedPeople);});

Используя фильтр json_script, вы гарантируете, что данные json правильно отформатированы и могут быть проанализированы Javascript без каких-либо проблем. Этот подход также помогает предотвратить потенциальные проблемы безопасности, связанные с XSS-атаками, путем надлежащего экранирования данных json.

Вернуться на верх