TypeError object is not iterable

2019-03-27 11:32发布

I am getting the following error when trying to loop over a variable in my Django templates. The variable in question is the related object of the model specified in my DetailView subclass:

TypeError at /en/applicants/50771459778/

'Householdmember' object is not iterable

Here is my models.py file:

class Applicant(models.Model):
    user              = models.ForeignKey(User, editable=False)
    bank_card_number  = models.CharField(_('Bank card number'),max_length=50, unique=True)
    site_of_interview = models.IntegerField(_('Site of interview'), choices = SITE_CHOICES, default=TIRANA, blank=False)
    housenumber       = models.CharField(_('House Number'),max_length=8)
    address_line1     = models.CharField(_('Address line 1'),max_length=50)
    address_line2     = models.CharField(_('Apt #'),max_length=50,blank=True) 
    municipality      = models.CharField(_('Municipality/commune'),max_length=25)
    district          = models.CharField(_('District'),max_length=25,blank=True)
    urban             = models.IntegerField(_('Area (urban/rural)'), choices = AREA_CHOICES, blank=False)
    postal            = models.CharField(_('Postal code'),max_length=25,blank=True) 

class Householdmember(models.Model):
    applicant         = models.ForeignKey(Applicant)
    first_name        = models.CharField(_('First name'),max_length=50,blank=False)
    middle_name       = models.CharField(_('Middle name'),max_length=50,blank=True) 
    last_name         = models.CharField(_('Last name'),max_length=50,blank=False)
    national_id       = models.CharField(_('National ID'),max_length=50,blank=False, unique=True)
    male              = models.IntegerField(_('Gender'), choices = GENDER_CHOICES, blank=False)
    date_of_birth     = models.DateField()
    rel_to_head       = models.IntegerField(_('Gender'), choices = RELTOHEAD_CHOICES, blank=False)
    disability        = models.IntegerField(_('Is disabled?'), choices = YESNO_CHOICES, blank=False)
    created_at        = models.DateTimeField(auto_now_add = True)
    updated_at        = models.DateTimeField(auto_now = True)

Here is my urls.py file:

class ListViewApplicants(ListView):
    paginate_by = 100
    def get_queryset(self):
        return Applicant.objects.all()

class DetailViewUnmask(DetailView):
    def get_object(self):
        return self.get_queryset().get(pk=mask_toggle(self.kwargs.get("pk_masked")))

urlpatterns = patterns('',
    url(r'^$',
        login_required(ListViewApplicants.as_view( 
                            template_name='applicants/index.html',
                            #context_object_name='form',
                            )),
        name='index'),
    url(r'^(?P<pk_masked>\d+)/$',
        login_required(DetailViewUnmask.as_view( model=Applicant,
                            template_name='applicants/detail.html'
                            )), 
        name='detail'),

Here is the relevant part of my template, detail.html:

<h2>Household members</h2>
<table class="package_detail">
    <tr>
        {% include "applicants/householdmember_heading_snippet.html" %}
    </tr>

    {% for householdmember in applicant.householdmember_set.all %}
    <tr>

        {% for field in householdmember %}
            <td>{{ field }}</td>
        {% endfor %}
        <!--
        <td>{{ householdmember.first_name }}</td>
        <td>{{ householdmember.middle_name  }}</td>
        <td>{{ householdmember.last_name  }}</td>
        <td>{{ householdmember.national_id  }}</td>
        <td>{{ householdmember.get_male_display }}</td>
        <td>{{ householdmember.date_of_birth }}</td>
        <td>{{ householdmember.get_rel_to_head_display }}</td>
        <td>{{ householdmember.get_disability_display }}</td>
        -->
    </tr>
    {% endfor %}
</table>

The part that is commented out (i.e. the part in between the <!-- --> tags) works, which leads me to think that I should be able to iterate over the householdmember variable. But when I try to do so, it doesn't work - I just get the TypeError above.

I have searched stackoverflow.com exentsively for an answer, but the closest answer I could find is this one: django how to loop through the context object passed back by a generic detailview?, but it does not solve my problem, I think because I'm using class based views.

Would greatly appreciate any help. Thanks!

2条回答
男人必须洒脱
2楼-- · 2019-03-27 12:14

I managed to solve this; here is how. I used info from here: Iterate over model instance field names and values in template

Here is what I added to my models.py file:

def get_all_fields(self):
    fields = []
    for f in self._meta.fields:
        fname = f.name        
        # resolve picklists/choices, with get_xyz_display() function
        get_choice = 'get_'+fname+'_display'
        if hasattr( self, get_choice):
            value = getattr( self, get_choice)()
        else:
            try :
                value = getattr(self, fname)
            except User.DoesNotExist:
                value = None

        # only display fields with values and skip some fields entirely
        if f.editable and f.name not in ('id', 'created_at', 'updated_at', 'applicant'):

            fields.append(
                {
                'label':f.verbose_name, 
                'name':f.name, 
                'value':value,
                }
            )
    return fields

And here is what my detail.html file ended up looking like:

<table class="package_detail">
    <tr>
        {% include "applicants/householdmember_heading_snippet.html" %}
    </tr>
    {% for householdmember in applicant.householdmember_set.all %}
    <tr>    
    {% for field in householdmember.get_all_fields %}
        <td>{{ field.value }}</td>
    {% endfor %}
    </tr>
    {% endfor %}
</table>

And this gives the desired output.

查看更多
狗以群分
3楼-- · 2019-03-27 12:15

You can't iter over a model instance. I recommend you use your commented code.

If you still want to use a forloop, maybe you can add this code:

class Householdmember(models.Model):
    # all yuur fields...

    def __iter__(self):
        return return [field.value_to_string(self) for field in Householdmember._meta.fields]

But, no one recommend that

That's better:

class Householdmember(models.Model):
    # all yuur fields...

    def __iter__(self):
        return [ self.first_name, 
                 self.middle_name, 
                 self.last_name, 
                 self.national_id, 
                 self.get_male_display, 
                 self.date_of_birth, 
                 self.get_rel_to_head_display, 
                 self.get_disability_display ] 
查看更多
登录 后发表回答