1

I am trying to create a form in which I want the user to give some option as an image and the user has to choose in b/w them but I have no idea how to do it I place an image in HTML show the user the image but I want to save that image option in my personal readme database also

here is my code

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 self.img_link

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

as you can see I want to give the user a system choice on by using a model which store the information like name image link and link of that system which they like to work on but instead of the name I want to give image option that why I am using image link so in my HTML I can view it with img src tag but unable to do it Any idea will helpful

HTML

<form action="" method="POST" enctype="multipart/form-data">
    {% csrf_token %}
    {{ form.name|as_crispy_field }}
    {{ form.about_me|as_crispy_field }} 
    {{ form.work_status|as_crispy_field }}
    {{ form.work_status_link|as_crispy_field }}
    <img src="{{ form.system|as_crispy_field }}" alt="">    <input type="submit" value="Genrate File">
  </form>

output enter image description here

as you can see it's putting URL but I want to show image instead of url

views.py

def home(request):
    if request.method == 'POST':
        form = Personal_Readme_form(request.POST)
        if form.is_valid():
            form.save()
            return redirect('request:preview')
    else:
        form = Personal_Readme_form()
    return render(request, 'home.html', {'form': form})

forms.py

class Personal_Readme_form(forms.ModelForm):
    class Meta:
        model = Personal_readme
        fields = '__all__'
        labels = {
            'name':'Your Name',
            'about':'About Yourself',
            'work_status':'Your Current work status',
            'resume_link':'Your Resume',
            'work_status':'Your current status',
            'system':'I prefer working on',
        }
        widgets = {
            'name': forms.TextInput(attrs={'placeholder': 'Type your name'}),
            'about_me': forms.Textarea(attrs={'placeholder': 'A short summary about yourself'}),

            'project1': forms.TextInput(attrs={'placeholder':'Name of your project'}),
    
            'project2': forms.TextInput(attrs={'placeholder':'Name of your project'}),
    
            'project3': forms.TextInput(attrs={'placeholder':'Name of your project'}),
    
            'project4': forms.TextInput(attrs={'placeholder':'Name of your project'}),
    
            'project5': forms.TextInput(attrs={'placeholder':'Name of your project'}),
    
            'work_status' : forms.TextInput(attrs={'placeholder': 'Your current status'}),

            'system' : forms.CheckboxSelectMultiple(),   
            
        }
Gonçalo Peres
  • 11,752
  • 3
  • 54
  • 83
Shreyash mishra
  • 738
  • 1
  • 7
  • 30

3 Answers3

3

(1) Irrespective of whether you are using crispy_forms or not, the simplest hack would be changing the __str__() of the model SystemChoice. Widget for field 'system' being: 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) Another way would be creating a custom crispy_form Field and rendering the form using {% crispy form %}. Rendering individual field in crispy form using {{ form.system|as_crispy_field }} will not render the field as the way {% crispy form %} renders the form fields.

Here, crispy_forms templates need to be modified. So, create new templates and point to that template by creating a new crispy_form Field. In form initialisation method call FormHelper and wrap the system field with custom Field. In this case: calling {{ form.system|as_crispy_field }} in template file will not render system field correctly. So will need to call {% crispy form %}. Which means to update form template file as well.

<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

Achuth Varghese
  • 2,356
  • 1
  • 4
  • 18
2

Creating a model for system variables is more straightforward and easy to manage.

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

and you can just do this in your template

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

Solution for storing images out but database

If you don't want to store images in the database you can use Cloudinary

pip install cloudinary

settings.py

import cloudinary
import cloudinary.uploader
import cloudinary.api

INSTALLED_APPS = [
    ...
    'cloudinary'
]

cloudinary.config( 
  # Get them from **Cloudinary dashboard page**
  cloud_name = "YOUR_CLOUD_NAME", 
  api_key = "YOUR_API_KEY", 
  api_secret = "YOUR_API_SECRET" 
)

models.py

from cloudinary.models import CloudinaryField

class SystemChoice (models.Model):
    name = models.CharField(max_length=200)
    img = CloudinaryField('image')
    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

class photos(models.Model):

Migrate

python manage.py makemigrations
python manage.py migrate

and template is same as it is:

{% for obj in your_context_for_Personal_readme.system.all %}
        {{ obj.name  }}
        <img src="{{ obj.img.url}}">
{% endfor %}
enes islam
  • 1,054
  • 4
  • 12
  • @nigel239 sorry? What is it about logic? and it is worst. I didn't try to get the image name in there. – enes islam Sep 05 '22 at 07:49
  • what if I use an image link instead of using an actual image how can I put in templates so it shows image and by that I way I don't have to store the image any idea because I am unable to bring image in templates – Shreyash mishra Sep 05 '22 at 13:54
  • i am using model form – Shreyash mishra Sep 05 '22 at 13:55
  • I have shown you how you can use Cloudinary for storing images on the cloud. I hope this will help you – enes islam Sep 06 '22 at 07:07
  • you are telling me how to retrieve the images from the database and show them on the template but I want to know a way by giving a form to the user so he can select the option of images and save that information that he selected windows and linux images for it preferred working system – Shreyash mishra Sep 07 '22 at 05:06
  • updated my question please review it again – Shreyash mishra Sep 07 '22 at 05:36
1

There are multiple ways to do that depending on what exactly OP wants to do in the models, templates, etc.

In the level of models, OP can

  1. Use choices attribute in the model field. Use this method if one knows that the images won't change. OP might want to change OP's system model field to CharField or FilePathField. Read more about choices here.

  2. Create a specific model / database table with a ForeignKey, or like enes islam mentions in the other answer. I prefer this method since it gives more flexibility in case one wants to add/edit/remove images.

Once the model is covered, there are different ways one can display it. For instance

  1. Fill the form field dynamically when the form is instantiated.

  2. Render choices manually. This answer has a relatively easy to understand overview.

Gonçalo Peres
  • 11,752
  • 3
  • 54
  • 83