Вопрос

I have invoice/estimates django app, I need to write tests for it. Since I'm a beginner with this, testing forms results hard for me. This is the code - for models, form and admin:

# MODELS 
class Invoice(models.Model):
    subject = models.ForeignKey(
    Subject,
    verbose_name= _("Subject")
    )
    date = models.DateField(default=date.today())
    tasks = models.ManyToManyField(
    Task,
    through='TaskCount',
    )
    discount = models.DecimalField(
    max_digits=10, 
    decimal_places=2, 
    default=0
    )
    tip1 = models.BooleanField();
    tip2 = models.BooleanField();
    tax_a = models.BooleanField();
    notes = models.CharField(max_length=500, blank=True)
    expire = models.DateField(null=True, blank=True)
    invoicenumber = models.IntegerField("Invoice number")
    subtotal = models.DecimalField(max_digits=10, decimal_places=2,)
    amount = models.DecimalField(max_digits=10, decimal_places=2, default=0)
    adi_start_date = models.DateField(null=True, blank=True)
    adi_end_date = models.DateField(null=True, blank=True)


class People(models.Model):
    first_name = models.CharField(
        "First name",max_length=50, blank=True
    )
    last_name = models.CharField(
        "Last name",max_length=50, blank=True
    )
    ....
    .... # other fields
    def __unicode__(self):
    return u"%s %s" % (self.first_name, self.last_name)

# model for inlines
class TaskCount(models.Model):
    count_items = models.PositiveIntegerField("Qty", default=1)
    task = models.ForeignKey(
    'Task',
    null=False)
    estimate = models.ForeignKey(
        'Estimate', 
        null=True, 
        blank=True
    )
    invoice = models.ForeignKey(
        'Invoice',
        related_name='task_count',
        null=True,
        blank=True
    )

    def __unicode__(self):
        return u"%s" % (str(self.task.price))

class Task(models.Model):
    label = models.CharField(
        "Task name",
        max_length=150
    )
    price = models.DecimalField(
        "Price per session", 
        max_digits=10, 
        decimal_places=2,
        default=0
    )
    is_adi = models.BooleanField(
        default=False,
        help_text=_("")
    )

# FORMS

class InvoiceAdminForm(forms.ModelForm):
    class Meta:
        model = Invoice

    def __init__(self, *args, **kwargs):
        super(InvoiceAdminForm, self).__init__(*args, **kwargs)
    ....

# ADMIN

# inline
class TaskCountInline(admin.TabularInline):
    model = TaskCount
    extra = 0
    fields = ('num_items', 'task')

    def __init__(self, *args, **kwargs):
        super(TaskCountInline, self).__init__(*args, **kwargs)

    ...

class InvoiceAdmin(admin.ModelAdmin):
    list_display = ('subject', 'date', 'amount',
        'discount', 'invoicenumber','get_pdf',
        'edit_item', 'delete_item')
    list_display_links = ('edit_item',)
    filter_horizontal = ('tasks',)
    search_fields = ['subject__first_name','subject__last_name']
    actions = ['create_pdf']
    inlines = (TaskCountInline,)
    form = InvoiceAdminForm

    fieldsets = (
    .... # fieldsets ovverride
    )

    formfield_overrides = {
    models.CharField: {'widget': forms.Textarea},
    }

    def get_pdf(self, obj):
        opts = self.model._meta
        return '<a href=%(reverse_url)s>' \
               '<img src="/static/admin/img/blue-document-pdf.png"/>' \
               '</a>' % {
           'reverse_url':reverse(
                       'admin:%s_%s_pdf' % (
                            opts.app_label, 
                            opts.object_name.lower()),
            args=(obj.id,)
            ),
            }
    get_pdf.short_description = _("PDF")
    get_pdf.allow_tags = True

    def change_view(self, request, object_id, form_url='', extra_context=None):
    ...


    def get_urls(self):
        urls = super(InvoiceAdmin, self).get_urls()
        info = self.model._meta.app_label, self.model._meta.module_name
        extra_urls = patterns('',
            url(r'^(?P<object_id>\d+)/pdf/$',
                self.admin_site.admin_view(self.pdf_view),
                name='%s_%s_pdf' % info),
        )
        return extra_urls+urls

    def pdf_view(self, request, object_id, extra_context=None):
        """
        view for generating PDF objects
        """
        ....


    def get_form(self, request, obj=None, **kwargs):
        form = super(InvoiceAdmin, self).get_form(request, obj, **kwargs)
        form.base_fields['invoicenumber'].initial =self.__get_last_invoice_nr_plus_one()
        ....
        return form

    def __get_last_invoice_nr_plus_one(self):
        ...

I'm trying to test the InvoiceAdminForm. this is the test code:

class InvoiceAdminFormTestCase(TestCase):
def setUp(self):
    self.subject = Preople.objects.create(
    ...
    )
    self.task1 = Task.objects.create(
    ...
    )
    self.task2 = Task.objects.create(
    ...
    )

    self.data = {
    'subject':self.subject.id,
    'csrfmiddlewaretoken':csrf._get_new_csrf_key(),
    'tip1': 1,
    'task_count-0-task': self.task1.id,
    'subtotal': 67.00,
    'amount': 69.68,
    '_continue': 'Save and continue edit',
    'task_count-0-id': '',
    'task_count-__prefix__-task': '',
    'notes': 'example  notes',
    'task_count-0-invoice':'',
    'task_count-1-num_items': '1',
    'invoicenumber': '4',
    'task_count-__prefix__-invoice': '',
    'adi_end_date': '',
    'task_count-MAX_NUM_FORMS': '1000',
    'discount': '0',
    'dataìe': '25/11/2013',
    'task_count-1-id': '',
    'adi_start_date': '',
    'task_count-1-invoice': '',
    'task_count-0-num_items': '1',
    'task_count-TOTAL_FORMS': '2',
    'task_count-__prefix__-num_items': '1',
    'task_count-__prefix__-id': '',
    'task_count-INITIAL_FORMS': '0',
    'task_count-1-task': self.task2.id,
    'expire': ''
    }
    self.form = InvoiceAdminForm(data=self.data)

the test fails due to the lack of mandatory field 'tasks'. I can't explain this. I've tried to save the form using admin interface, and it worked without errors. Using ipdb debugger, I saved the data querydict and I used it in the form case. So same data for populating the form, but different results.

this is the traceback:

# ./manage.py test gpf2
Creating test database for alias 'default'...
======================================================================
FAIL: test_valid_form (gpf2.tests.test_forms.InvoiceAdminFormTestCase)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "/data_ext4/src/proj7/gpf2/tests/test_forms.py", line 113, in test_valid_form
self.assertTrue(self.form.is_valid(), msg=self.form.errors)
AssertionError: <ul class="errorlist"><li>tasks<ul class="errorlist"><li>This field is required..</li></ul></li></ul>

----------------------------------------------------------------------
Ran 13 tests in 0.205s

FAILED (failures=1)
Destroying test database for alias 'default'...

Any help on that?

thanks

Это было полезно?

Решение

You are using inlines for editing the related tasks, so the tasks field is removed by the admin from your form. If you use the form outside of the admin, the tasks field is still there, so you need to provide values. Or you could explicitly specify the fields on your form's Meta options, excluding the tasks field.

The admin generates a different form for its usage, to see this, try:

from django.test import RequestFactory
from django.contrib.auth.models import User
from django.contrib import admin

# import InvoiceAdmin and Invoice here...

rf = RequestFactory()
user = User.objects.get(username="a_user_with_sufficient_permissions")
request = rf.get("/admin/foo/")
request.user = user
a = InvoiceAdmin(Invoice, admin.site)
print a.get_form(request).Meta.fields  # the form as modified by the admin
print a.form.Meta.fields  # the form as specified by you
Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top