简体   繁体   中英

Django Model - Charfield with choices from a row in another model?

I have created a settings table in Django as per the below:-

class Settings(models.Model):
    name = models.CharField(max_length=200)

    class Meta:
        verbose_name = "Settings"
        verbose_name_plural = "Settings"    

    def __str__(self):
        return self.name  

class SettingChoices(models.Model):   
    setting = models.ForeignKey(Settings, on_delete=models.PROTECT) 
    choice = models.CharField(max_length=200)    
    class Meta:
        verbose_name = "Setting Choices"
        verbose_name_plural = "Setting Choices"    

    def __str__(self):
        return '{0} - {1}'.format(self.setting, self.choice)   

and a sample use of this would be:-

Setting = Circuit Type:
Choices:
 DSL
 4G
 Fibre

then in another model I want to be able to reference this as set of choices

class Circuits(models.Model):
    site_data = models.ForeignKey(SiteData, verbose_name="Site", on_delete=models.PROTECT)
    order_no = models.CharField(max_length=200, verbose_name="Order No")
    expected_install_date = models.DateField()
    install_date = models.DateField(blank=True, null=True)
    circuit_type = models.CharField(max_length=100, choices=*** here I would get model settings - Circuit Type - Choices ***)

currently I use a list in settings.py but its not fluid, I need my users to be able to alter these settings not for me to manually edit a list in settings.py and push changes each time

I attempted the below:

functions.py

def settings_circuit_types():
    from home.models import SettingChoices
    type_data = SettingChoices.objects.filter(setting__name='CIRCUIT_TYPES')
    circuit_types = []
    for c in type_data:
        circuit_types.append(c.choice)
    return circuit_types

models.py

from app.functions import settings_circuit_types
CIRCUIT_CHOICES = settings_circuit_types()
...
circuit_type = models.CharField(max_length=100, choices= CIRCUIT_CHOICES)

but this has thrown an error

dango.core.exceptions.AppRegistryNotReady: Models aren't loaded yet.

which is understandable, im wondering if what im trying to achieve is possible by other means?

Thanks

So this is a better way of doing this as i mentioned it in comment section:

1 - You don't need Settings and SettingChoices . They are basically the same so you can combine them into one model called Setting :

class Setting(models.Model):
    name = models.CharField(max_length=200)
    # if you need anything that you might think you need another model for,
    # just think this way, add a BooleanField.
    # for example if you have a setting available only for admins:

    only_admin = models.BooleanField(default=False)

    # then when you're going to make a from, just filter the options;
    # options = setting.objects.filter(only_admin=False)

    class Meta:
        verbose_name = "Settings"
        verbose_name_plural = "Settings"    

    def __str__(self):
        return self.name

2 - And for Circuits model you just need a simple ForeignKey field:

class Circuits(models.Model):
    site_data = models.ForeignKey(SiteData, verbose_name="Site", on_delete=models.PROTECT)
    order_no = models.CharField(max_length=200, verbose_name="Order No")
    expected_install_date = models.DateField()
    install_date = models.DateField(blank=True, null=True)
    circuit_type = models.ForeignKey(Setting, null=False, blank=False)

Now when you want to make a form for users to fill:

forms.py :

class CircuitsForm(forms.ModelForm):

    class Meta:
        model = Circuits
        fields = ('install_date', 'circuit_type') # or other fields.

    # and to filter which choices are available to choose from:
    def __init__(self, *args, **kwargs):
        super(CircuitsForm, self).__init__(*args, **kwargs)
        self.fields["circuit_type"].queryset = setting.objects.filter(only_admin=False)

This way you have a safe and easy way to make forms for both users and your admins.

You can edit the admin panel itself or just make a url just for admin users with a form like this.

Also if you aren't that kind of people who uses django to render their form you can simply get the available choices in your view and pass it to the template like this:

settings = setting.objects.filter(only_admin=False)

and render it in a template like this:

<select name="circuit_type">
    {% for setting in settings %}
        <option value="{{ setting.pk }}">{{ setting.name }}</option>
    {% endfor %}
</select>

Now you will have only choices you want them to show up in form and even if user tries to mess with template code and add more options, form won't allow them to be accepted and it will raise an error for it.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM