Search code examples
djangodjango-modelsdjango-admindjango-admin-filtersdjango-admin-tools

How to add both integer field and choices in a single attribute of a model class in django?


I want to add both choices of days and hours in my assigned_time field, along with adding how many days or how many hours. Here is what I tried that doesn't work. Here is my model.py

class Timesheet(models.Model):
    DAYS= 'D'
    HOURS = 'H'
    STATUS= [
        ('D', 'Days'),
        ('H', 'Hours')
    ]
    employee= models.ForeignKey(Employee, on_delete=models.CASCADE)
    date= models.DateField(verbose_name='Date')
    designation=models.ForeignKey(Designation, on_delete=models.CASCADE)
    projects= models.CharField(max_length=256, verbose_name='Project Name')
    assigned_time= models.IntegerField(verbose_name='Assigned Hours/days', choices=STATUS, default=DAYS)
    time_spent=models.IntegerField(verbose_name='Spent Hours/Days', choices=STATUS, default=DAYS)
    description=models.TextField(max_length=1000, verbose_name='Description')

    def __str__(self):
        return f'{self.employee.user.first_name} {self.employee.user.last_name} {self.project}' 

Check for assigned_time or time_spent field. I want to do the same thing on both as I explained above.

Here is my admin.py

@admin.register(Timesheet)
class TimesheetAdmin(admin.ModelAdmin):
    list_display=('first_name', 'last_name', 'date', 'assigned_time', 'projects', 'time_spent', 'description', 'designation_name')

    def first_name(self, obj):
        return obj.employee.user.first_name

    def last_name(self, obj):
        return obj.employee.user.last_name

    def designation_name(self, obj):
        return obj.designation.designation_name

Here is the image of what I am getting

enter image description here

What should I do to add both things in a single field?


Solution

  • I think you should create another field for type of time. You cannot have both in the same field. Even if it would be not readable for some reasons. I suggest such solution:

    Add another field of type and use CHOICES there:

    class Timesheet(models.Model):
        STATUS = [
            ('D', 'Days'),
            ('H', 'Hours')
        ]
    
        ...
        time_type = models.CharField(verbose_name='Type of time', choices=STATUS, default=STATUS[0], max_length=10)
        assigned_time = models.IntegerField(verbose_name='Assigned time', default=0)
        time_spent = models.IntegerField(verbose_name='Spent time', default=0)
        ...
    

    I also corrected your attempt for choices. It was not working, cause IntegerField cannot accept string values.

    UPDATE: If you really need have it in one field, you can use IntegerField choices like that:

    STATUS = [
        (1, '1 Hour'),
        (2, '2 Hours'),
        (3, '3 Hours'),
        ...
        (1, '1 Day'),
        (2, '2 Days'),
        (3, '3 Days'),
        ...
    ]
    
    # or comprehensions will give us same result with less code
    
    STATUS = [
    (i, f'{i} Days') for i in range(1, 31)
    ]
    
    [STATUS.append((i, f'{i} Hours')) for i in range(1, 25)]
    

    But remember, that first value of the tuple has to fit Field type.