Django ModelForm ChoiceField not displaying instance data - django

I have a ModelForm class in which I set a couple of the fields as ChoiceField. For one of my views, I'd like to create a form from my ModelForm class that pulls from an instance of my model in the database (like so):
form = MyModel(instance=model_instance)
When I do this and then render the form in a template, I've noticed that most of the fields are pre-populated with values pulled from the model instance, which is what I want. However, this isn't the case for two ChoiceField fields. These render as drop-down select menus with no specific option selected.
What's strange is if I don't define those two fields as ChoiceField-type in my ModelForm class, they render as normal text input fields in HTML and pre-populate using the database values. But when I define them so they show up as select-option input fields in HTML, nothing is pre-selected. Can I change this so that the values from the database are pre-selected?
EDIT: As requested here is the code for my model and form:
class App(models.Model):
CODES = (
(u'a',u'annual'),
(u'm',u'monthly'),
(u'w',u'weekly')
)
code = models.CharField(max_length=1, choices=CODES)
start_time = models.TimeField(blank=True, null=True)
end_time = models.TimeField(blank=True, null=True)
class AppForm(ModelForm):
CODES = (
(u'',u'Please select code'),
(u'a',u'annual'),
(u'm',u'monthly'),
(u'w',u'weekly')
)
TIMES = (
(u'00:00',u'All Day'),
(u'12:00',u'Noon')
)
start_time = forms.ChoiceField(required=False, choices=TIMES)
end_time = forms.ChoiceField(required=False, choices=TIMES)
code = forms.ChoiceField(choices=CODES, label='Type')
class Meta:
model = App
Interestingly, code field has the model instance value preselected just fine when rendered as HTML. I wonder if having the choices argument in the model definition makes the difference here?
UPDATE: I just noticed that if I pull up an App instance in the python manage.py shell like so:
a = App.objects.get(id=16)
a.start_time
I get a value like datetime.time(12, 0). But in the Django admin, when I'm looking at all of the App instances, all of them show (None) under start_time and end_time. Why would that be?

In response to your update : your times strings match default time string HH:MM format. Just like a user would enter them from website manually 12:00. The values get parsed and turned into time at model save (at validating really).
And when you load model - then of course the initial values loaded from object match the field's (models.TimeField) type.
If you replace your TIMES with
(datetime.time(0,0),u'All Day'),
(datetime.time(12,0),u'Noon')
your troubles should be over.
Alan

Related

Django Admin - Show form json input value as text insted string

Considering I have a model like:
MyStore = (
id = 1,
name = 'Foobar',
information_as_json = {
'open_at': datetime.now(),
'close_at': datetime.now() + timedelta('+1 day'),
'workers' : {
'Person1' : 'Owner',
'Person2' : 'Boss',
'Person3' : 'Boss',
}
})
Inside Django admin forms, for every field is generated an input, but for the field "information_as_json", I don't want to show it as a string or as JSON. That is because the users who are accessing this store admin page, need to read the field 'information_as_json' easier since no one can edit these values because it is generated in another part of the application.
Is it possible to convert these values to a "div" or a plain text? The contents would be:
This store opens at: {information_as_json.open_at}
This store close at: {information_as_json.close_at}
And for the workers, iterate through keys and values:
for key, value in information_as_json.workers:
Worker {key} has the role: {value}
I'm a beginner at Django, so I'm struggling a little with this part.
Every help would be appreciated :D
I would suggest approaching the model a little differently. Rather than storing the opening and closing hours as JSON they can just be fields directly on the store model. The the workers can be a JSONfield [docs] containing name/role pairs. If you're using PostgreSQL for your database you could even use HStoreField [docs], which might be more appropriate.
Here's how I would write a similar model.
class Store(models.Model):
name = models.CharField(max_length=512, unique=True)
workers = models.JSONField(blank=True, default=dict, editable=False)
closing = models.TimeField(blank=True, null=True, editable=False)
opening = models.TimeField(blank=True, null=True, editable=False)
To display the details in the Django admin we just need to define a property which returns the correct string.
#mark_safe
def details(self):
roles = [
f'{x} has the role: {y}'
for x, y in self.workers.items()
]
return '<br>'.join([
f'This store opens at: {self.opening:%-H:%M}',
f'This store closes at: {self.closing:%-H:%M}',
] + roles)
This method can then be referenced in the ModelAdmin and used like a read-only field.
#admin.register(Store)
class StoreAdmin(admin.ModelAdmin):
list_display = ['name', 'opening', 'closing']
fields = ['name', 'details']
readonly_fields = ['details']

Cannot resolve added property of model in views

In my views.py file, I filter objects from a model, and I add some properties to the objects to use them later in templates.
matches = Match.objects.filter(season=season_name).order_by("match_date")
team_list = Team.objects.filter(id__in=team_ids)
for team in team_list:
home_matches = matches.filter(home_team=team)
away_matches = matches.filter(away_team=team)
team.sf = len(Shot.objects.filter(match__in=matches, team=team))
team.sa = len(Shot.objects.filter(match__in=matches, opponent_team=team))
team.pdo = ((team.f / team.sf) + (1 - (team.a / team.sa))) * 1000
team.xpdo = ((team.xgf / team.sf) + (1 - (team.xga / team.sa))) * 1000
For example, in my team model, fields sf, sa, pdo and xpdo does not exist. I calculate those fields on views and use them in a table in my template. There is no problem until here, actually. What I am trying to do is dumping query set as JSON and passing it the template to use in JavaScript.
context["teams_json"] = json.dumps(list(team_list.values_list(
"team_name", "pdo", "xpdo")), cls=DjangoJSONEncoder)
When I run the server, I get the following error:
FieldError at /leagues/turkey2017/
Cannot resolve keyword 'pdo' into field. Choices are: away_team, home_team, id, opponent_team, passmaplineup, passmappass, player_stats_opponent_team, player_stats_team, shooter_team, team_dark_color, team_light_color, team_name
I see that choices are combination of original fields in my Team model and ForeignKey fields in other models such as this field in Shot model:
opponent_team = models.ForeignKey(
Team, related_name="opponent_team", on_delete=models.CASCADE)
How can I use the fields added in views later on in views again?
The problem is that you are trying to use values_list on something that is not a field. I suppose that the reason you are doing so is to remove unnecessary fields from your serialized data. But really using values_list at all here is overkill; you just have three bits of data, you could just as easily dump them manually:
data = [{'pdo': team.pdo, 'xpdo': team.xpdo, 'team_name': team.team_name} for team in team_list]
context["teams_json"] = json.dumps(data)

Django validate data when updating model with primary key

I am having trouble with updating fields of a model instance. The model is as follows:
class commonInfo(models.Model):
mothers_id = models.IntegerField(primary_key=True)
date = models.DateField()
data_collector = models.CharField(max_length=50)
Essentially, I just want to do this, but it won't work because commonInfo has a user defined primary key
commonInfo_form(request.POST or None).is_valid()
Since I am updating, I am overriding date and data_collector, but not mothers_id. So I would want to do something like this, but this specific code is not working
obj = commonInfo.objects.get(pk=commonInfo_id)
form = commonInfo_form(request.POST)
date = form.cleaned_data['data_collector'] #this line is not working
data_collector = form.cleaned_data['data_collector'] #this line is not working
obj.update(**{'date':date, 'data_collector':data_collector})
any ideas? I feel like it is just those two lines that I need to fix. Or if there is a more pythonic way or built method in Django?
Just validate with isinstance. so like,
if isinstance(request.POST['date'], datetime.date) and isinstance(request.POST['data_collector'], str):
# you might have to use getattr for request.POST here, I'm not sure
# and request.POST['date'] would have to be converted from a string to datetime.date I think
date = request.POST['date']
data_collector = request.POST['data_collector']
obj.update(**{'date':date, 'data_collector':data_collector})
The process for adding a record from a form is different from updating an existing instance. All you need to do differently is indicate which instance to bind the form to when you create it, ex:
obj = commonInfo.objects.get(pk=commonInfo_id)
form = commonInfo_form(request.POST, instance=obj)

django skip a field's validation

I need to skip validation of the OrderAmount field but for it to still save the invalidated data. Is there a way this can be done? I know django allows you to make your own validation, but I don't know how to make it completely skip just one field's validation.
model:
class LiquorOrder(models.Model):
pack_size = (
('1', '1'),
('2', '2'),
)
LiquorOrderID = models.AutoField(primary_key=True)
storeliquorID = models.ForeignKey(StoreLiquor)
orderID = models.ForeignKey(Order)
OrderAmount = models.PositiveSmallIntegerField('Order Amount', max_length=3, choices=pack_size)
TotalPrice = models.DecimalField('Total Price', max_digits=5, decimal_places=2)
Form:
class AddToOrderForm(forms.ModelForm):
class Meta:
model = LiquorOrder
fields = ('OrderAmount',)
For a PositiveSmallIntegerField the only validation Django does is ensure the value is indeed a positive integer within the appropriate range. If you were to skip this, you would run into problems when Django tries to write the value to your database. If you were to, say, try to write the value "marshmallows" to a DB column that's expecting an integer, the DB will throw errors and Django will turn around and throw you an IntegrityError.
If you really wanted to try, you could override the field to be CharField with required set to False (basically allowing any keyboard input):
class AddToOrderForm(forms.ModelForm):
OrderAmount = forms.CharField(required=False)
Django would then return True when you run is_valid(), but throw errors when you try to call save().
It sounds like your real issue is with your model not matching your current project requirements. If that is the case, look into migrating to a new model. The Python library South is brilliant tool for this purpose and is used heavily by the Django community. I would read up on DB migrations and see if you can come up with a solution that way.

modelformset_factory excludes too many fields

I think it may be a bug in modelformset_factory in Django 1.2, but I'd like anyone else to double check that (possibly in newer djnago versions as well).
Just for courious people - models are representing possible scores, and sets of those available for worker's training reports.
models.py:
class ScoreSet(Model):
unit = ForeignKey(Unit)
description = CharField(max_length=20, verbose_name='description')
class Score(Model):
scoreset = ForeignKey(ScoreSet)
score = CharField(max_length=8, verbose_name='score')
description = CharField(max_length=30, verbose_name='description')
and now:
FormSet = modelformset_factory(Score, exclude='scoreset', extra=5, can_delete=True)
will produce formset with no scoreset and no score field. If you change "scoreset" field name to anything else (eg. "ss") it works fine, and excludes only "ss" field.
Exclude should be a tuple, not a string:
exclude=('scoreset',)