Giter VIP home page Giter VIP logo

Comments (9)

michaelmob avatar michaelmob commented on May 30, 2024

How are you building the choices for the CharField? Is there a different form field that converts the values to a ChoiceField and back?

Can you show a code example?

from django-semanticui-forms.

DanielSwain avatar DanielSwain commented on May 30, 2024

We are sending quotes to customers, and each quote has one of a customer's contacts associated with it. Note in the first model below that recipient is a CharField. We are filling it with comma-separated values (email addresses), and we also allow users to add emails that aren't in the database.

class EmailMessage(models.Model):
    sender = models.CharField(max_length=255, null=True, blank=True)
    recipient = models.CharField(max_length=255, null=True, blank=True)
    subject = models.CharField(max_length=255, null=True, blank=True)
    content = models.TextField(null=True, blank=True)

class ContactMethod(BaseModel):
    contact = models.ForeignKey(Contact)
    method_type = models.ForeignKey(ContactMethodType, null=True, blank=True)
    label = models.CharField(max_length=255, null=True, help_text="Further clarification of type e.g. 'Home', 'Work', etc.", blank=True)
    value = models.CharField(max_length=255, null=True, blank=True)
    is_active = models.BooleanField(default=True, editable=False)

Contact method types are email, phone, fax. The form field is rendered like this:

<script type="text/javascript">
  $(function(){
    $('#recipient').dropdown({allowAdditions: true});
  });
</script>

{% for field in form %}
    {% if field.name == 'recipient' %}
        <div class="inline field">
            <label for="recipient">To</label>
            <div id="recipient" class="ui multiple search selection dropdown">
                <input name="recipient" type="hidden" value="{{ field.initial }}">
                <i class="dropdown icon"></i>
                <input class="search" autocomplete="off" tabindex="0">
                <div class="default text">Choose or enter email(s)</div>
                <div class="menu">
                    {% for e in quote.contact.active_emails %}
                <div class="item" data-value="{{ e.value }}">{{ e.contact.first_name }} {{ e.contact.last_name }}</div>
              {% endfor %}
                </div>
            </div>
        </div>
    {% else %}
        {% render_field field _inline=1 %}
    {% endif %}
{% endfor %}

Note this in the above: <div class="item" data-value="{{ e.value }}">. Instead of using the id of the contact method, we're using the value (in this case the email address since we're only working with emails). Therefore, it is the above data-value into which I would like to be able to place the value of the value field of the ContactMethod model.

from django-semanticui-forms.

michaelmob avatar michaelmob commented on May 30, 2024

While this would ideally be worked into EmailMessage's ModelForm itself, it's less code and easier just to put it in the view.
I'm not at my dev desktop right now to test anything out, but I think you could do something like this.

def my_view(request):
    form = EmailSomethingModelForm()
    quote = Quote.objects.filter(...).first()
    form.fields["recipient"] = forms.ModelMultipleChoiceField(
        queryset=quote.contact.active_emails, to_field_name="value"
    )

Let me know if that works for you
utils.py#L46

from django-semanticui-forms.

DanielSwain avatar DanielSwain commented on May 30, 2024

Thanks for pointing me in the right direction - I wasn't familiar with to_field_name. When I tried your suggestion in views, it gave this error message, which I understand: 'instancemethod' object is not iterable. So therefore it doesn't look like I can set it in the view. I did prefer to add the declaration to forms.py anyway, and I was already setting the queryset in forms.py, so I just set it up in forms like this:
Under Meta:
widgets={ 'recipient': forms.SelectMultiple}

In init and after super():
self.fields['recipient'].queryset = contact.active_emails()
self.fields['recipient'].to_field_name = 'value'

HOWEVER, while the recipient field (declared in models.py as models.CharField) is rendered correctly as a "ui multiple selection dropdown", it WILL NOT render the menu when I use {% render_form form %}. It just renders an empty <div class="menu"></div>. Could this be a tiny shortcoming in django-semanticui-forms? Here is the only thing that it renders - copied straight from the rendered code (even though there are two results in the queryset [I've written that to a log to verify it] and the same two results are initialized in the recipient field):

    <div class=" field">
        <label for="id_recipient">Recipient</label>
        <div class="ui multiple selection dropdown" tabindex="0">
            <select multiple="multiple" class="search" id="id_recipient" maxlength="255" name="recipient"></select>
            <i class="dropdown icon" tabindex="0">
                <div class="menu" tabindex="-1"></div>
            </i>
            <div class="default text">Select</div>
            <div class="menu" tabindex="-1"></div>
        </div>
    </div>

Odd that it's rendering two empty menus - and one of them is within tags. Also, when I use my previously set up html for hand-rendering the field, it works fine with the above in forms.py - so it seems that the problem has to be in the rendering of the SelectMultiple on an element that is declared as models.CharField.

from django-semanticui-forms.

michaelmob avatar michaelmob commented on May 30, 2024

I see what's happening here. queryset won't be evaluated because it's not a Model* field. When you're changing the widget to SelectMultiple, there are no "choices" set so that's why there are no options.

Select / SelectMultiple widgets are made to put out select and option nodes.
And also confusingly, SelectMultiple widget is only for an array of values meaning Django will tell you that a custom entry is invalid.

However, you can use a TextInput widget, and override the wrapper to use the dropdown wrapper. Afterwards changing the style to contain "multiple" and "search" should give you the results you want.

from django.db.models import Value
from django.db.models.functions import Concat



class ModelForm(forms.ModelForm):
    class Meta:
        widgets = {"recipient": forms.TextInput(attrs={
            "_style": "search multiple", "_override": "Select"
        })}


    def __init__(self, *args, **kwargs):
        super(__class__, self).__init__(*args, **kwargs)
        self.fields["recipient"]._choices = (
            contact.active_emails()
                .prefetch_related("contact")
                .annotate(full_name=Concat("contact__first_name", Value(" "), "contact__last_name"))
                .values_list("value", "full_name")
        )

Let me know!

from django-semanticui-forms.

DanielSwain avatar DanielSwain commented on May 30, 2024

Getting the error below. Below this first screen shot is another that could be helpful. Should line 64 in your utils.py be choices._insert(...) ?

image

image

from django-semanticui-forms.

michaelmob avatar michaelmob commented on May 30, 2024

Oops! Try making the queryset an actual list

self.fields["recipient"]._choices = list(
            contact.active_emails()
                .prefetch_related("contact")
                .annotate(full_name=Concat("contact__first_name", Value(" "), "contact__last_name"))
                .values_list("value", "full_name")
        )

from django-semanticui-forms.

DanielSwain avatar DanielSwain commented on May 30, 2024

That did it except for one tiny item - it is automatically rendering a '--------' as the first item in the dropdown. I don't think that this should happen for a SUI multiple dropdown. At this point I'm just removing it upon page load with JS.

Also, the ContactMethod model has a 'label' field that can be used to designate 'home', 'work', etc. Therefore, the final rendering ended up like this:

            self.fields["recipient"]._choices = list(
                contact.active_emails()
                    .prefetch_related("contact")
                    .annotate(full_name=Case(
                        When(Q(label__isnull=False),
                            then=Concat("contact__first_name", Value(" "), "contact__last_name", Value(' ('), "label", Value(")"))
                        ),
                        default=Concat("contact__first_name", Value(" "), "contact__last_name")
                    )
                ).values_list("value", "full_name")
            )

That lets us, in the dropdown, distinguish between the types of emails for the same person.

Thank you for your incredible responsiveness and help on this. The above seems pretty deep to me - it's a shame this wasn't on StackOverflow. If you'll reply to acknowledge receipt of this final message, then I'll close it out. Thanks again!

from django-semanticui-forms.

michaelmob avatar michaelmob commented on May 30, 2024

Cool! I'm glad it worked out for the most part.

In a bit I'll take a look at the empty value being thrown in there.

from django-semanticui-forms.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.