Как использовать изображение и встроенную ссылку в качестве опции в форме модели django

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

вот мой код

class Personal_readme(models.Model):
    system_choice = [
        ('windows', 'windows'),
        ('linux', 'linux'),
        ('macOs', 'macOs'),
        ('unix', 'unix')
    ]
    work_status_Regex = RegexValidator(regex = "((http|https)://)(www.)?[a-zA-Z0-9@:%._\\+~#?&//=]{2,256}\\.[a-z]{2,6}\\b([-a-zA-Z0-9@:%._\\+~#?&//=]*)")
    name = models.CharField(max_length=70, blank=False)
    about_me = models.CharField(max_length=100, blank=True)
    work_status =  models.CharField(max_length=70, blank=True)
    work_status_link = models.URLField(validators = [work_status_Regex], blank=True)
    system = MultiSelectField(max_length=20, choices=system_choice,max_choices=4, blank=True )


    def __str__(self):
        return self.name

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

Создание модели для системных переменных более простое и легко управляемое

class SystemChoice (models.Model):
    name = models.CharField(max_length=200)
    img =  models.ImageField(default='default.png', upload_to='OS_logos',null=False, blank=False)
    link = models.URLField(max_length=400)

class Personal_readme(models.Model):
    work_status_Regex = RegexValidator(regex = "((http|https)://)(www.)?[a-zA-Z0-9@:%._\\+~#?&//=]{2,256}\\.[a-z]{2,6}\\b([-a-zA-Z0-9@:%._\\+~#?&//=]*)")
    name = models.CharField(max_length=70, blank=False)
    about_me = models.CharField(max_length=100, blank=True)
    work_status =  models.CharField(max_length=70, blank=True)
    work_status_link = models.URLField(validators = [work_status_Regex], blank=True)
    system = models.ManyToManyField(SystemChoice, blank=True)

    def __str__(self):
        return self.name

и вы можете просто сделать это в своем шаблоне

{% for obj in your_context_for_Personal_readme.system.all %}
        {{ obj.name  }}
        <img src="{{ obj.img.link }}">
{% endfor %}

Есть несколько способов сделать это, в зависимости от того, что именно ОП хочет сделать в моделях, шаблонах и т.д.

На уровне моделей, ОП может

  1. Используйте атрибут choices в поле модели. Используйте этот метод, если известно, что изображения не будут меняться. Возможно, ОП захочет изменить системное поле модели ОП на CharField или FilePathField. Читайте больше о выборе здесь.

  2. Создать конкретную модель / таблицу базы данных с ForeignKey, или как enes islam упоминает в другом ответе. Я предпочитаю этот метод, поскольку он дает больше гибкости в случае, если нужно добавить/редактировать/удалить изображения.

После того, как модель покрыта, существуют различные способы ее отображения. Например

  1. Заполните поле формы динамически при инстанцировании формы.

  2. Рендеринг вариантов вручную. В этом ответе есть относительно простой для понимания обзор.

(1) Независимо от того, используете ли вы crispy_forms или нет, простейшим хаком будет изменение __str__() модели SystemChoice. Виджет для поля 'system' будет: forms.CheckboxSelectMultiple.

models.py

# other imports
from django.utils.html import format_html

# other code statements

class SystemChoice (models.Model):
    name = models.CharField(max_length=200)
    img_link = models.URLField(blank=False)
    link = models.URLField(blank=False)

    def __str__(self):
        return format_html(f"{self.name}<br><img src='{self.img_link}' width='100px' />")

enter image description here

(2) Другим способом будет создание пользовательского поля crispy_form Field и рендеринг формы с помощью {% crispy form %}. Рендеринг отдельного поля в crispy форме с помощью {{ form.system|as_crispy_field }} не будет рендерить поле так, как это делает {% crispy form %} при рендеринге полей формы.

Здесь необходимо изменить шаблоны crispy_forms. Поэтому создайте новые шаблоны и укажите на этот шаблон, создав новое поле crispy_form Field. В методе инициализации формы вызовите FormHelper и оберните поле system пользовательским Field. В данном случае: вызов {{ form.system|as_crispy_field }} в файле шаблона не будет корректно отображать системное поле. Поэтому необходимо будет вызвать {% crispy form %}. Что означает обновление файла шаблона формы.

<form action="" method="POST" enctype="multipart/form-data">
    {% crispy form %}
    <input type="submit" value="Genrate File">
</form>

forms.py

# other imports
from crispy_forms.helper import FormHelper
from crispy_forms.layout import Field

# other code statements

class CrispyCheckboxImageSelectMultiple(Field):
    template = "<PATH_TO_APP_TEMPLATES_DIR>/checkbox_multiple_images.html"


class Personal_Readme_form(forms.ModelForm):
    class Meta:
        # other code statements
        widgets = {
            # other field widgets
            "system": forms.CheckboxSelectMultiple(),
        }

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.helper = FormHelper(self)
        self.helper['system'].wrap(CrispyCheckboxImageSelectMultiple)

checkbox_multiple_images.html

{% load crispy_forms_field %}
{% load l10n %}

{% if field.is_hidden %}
    {{ field }}
{% else %}

    <div class="form-group{% if 'form-horizontal' in form_class %} row{% endif %}">
    {% if label_class %}
        <div class="{% for offset in bootstrap_checkbox_offsets %}{{ offset }} {% endfor %}{{ field_class }}">
    {% endif %}
        
    <{% if tag %}{{ tag }}{% else %}div{% endif %} id="div_{{ field.auto_id }}" class="{% if not field|is_checkbox %}form-group{% if 'form-horizontal' in form_class %} row{% endif %}{% else %}{%if use_custom_control%}custom-control custom-checkbox{% else %}form-check{% endif %}{% endif %}{% if wrapper_class %} {{ wrapper_class }}{% endif %}{% if field.css_classes %} {{ field.css_classes }}{% endif %}">
        {% if field.label and not field|is_checkbox and form_show_labels %}
        {# not field|is_radioselect in row below can be removed once Django 3.2 is no longer supported #}    
        <label {% if field.id_for_label and not field|is_radioselect %}for="{{ field.id_for_label }}" {% endif %}class="{% if 'form-horizontal' in form_class %}col-form-label {% endif %}{{ label_class }}{% if field.field.required %} requiredField{% endif %}">
                {{ field.label|safe }}{% if field.field.required %}<span class="asteriskField">*</span>{% endif %}
            </label>
        {% endif %}

        {% include '<PATH_TO_TEMPLATES_DIRECTORY_OF_YOUR_APP>/checkbox_image_options.html' %}

    </{% if tag %}{{ tag }}{% else %}div{% endif %}>

    {% if label_class %}
        </div>
    {% endif %}
    </div>
{% endif %}

checkbox_image_options.html

{% load crispy_forms_filters %}
{% load l10n %}

<div here {% if field_class %}class="{{ field_class }}"{% endif %}{% if flat_attrs %} {{ flat_attrs|safe }}{% endif %}>

    {% for group, options, index in field|optgroups %}
        {% if group %}<strong>{{ group }}</strong>{% endif %}
        {% for option in options %}
        <div class="{%if use_custom_control%}custom-control custom-checkbox{% if inline_class %} custom-control-inline{% endif %}{% else %}form-check{% if inline_class %} form-check-inline{% endif %}{% endif %}">
            <input type="checkbox" class="{%if use_custom_control%}custom-control-input{% else %}form-check-input{% endif %}{% if field.errors %} is-invalid{% endif %}" name="{{ field.html_name }}" value="{{ option.value|unlocalize }}" {% include "bootstrap4/layout/attrs.html" with widget=option %}>
            <label tester class="{%if use_custom_control%}custom-control-label{% else %}form-check-label{% endif %}" for="{{ option.attrs.id }}">
                
                {% comment %} Image tag added here {% endcomment %}
                <img src="{{ option.label }}" width="100px" />

            </label>
            {% if field.errors and forloop.last and not inline_class and forloop.parentloop.last %}
                {% include 'bootstrap4/layout/field_errors_block.html' %}
            {% endif %}
        </div>
        {% endfor %}
    {% endfor %}

    {% if field.errors and inline_class %}
    <div class="w-100 {%if use_custom_control%}custom-control custom-checkbox{% if inline_class %} custom-control-inline{% endif %}{% else %}form-check{% if inline_class %} form-check-inline{% endif %}{% endif %}">
        {# the following input is only meant to allow boostrap to render the error message as it has to be after an invalid input. As the input has no name, no data will be sent. #}
        <input type="checkbox" class="custom-control-input {% if field.errors %}is-invalid{%endif%}">
        {% include 'bootstrap4/layout/field_errors_block.html' %}
    </div>
    {% endif %}

    {% include 'bootstrap4/layout/help_text.html' %}
</div>

enter image description here

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