Значения 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.