Delete field from standard Django model - django

NOTE: this was asked before AbstractUser existed, which is probably what you'd want to use these days.
Basically I would like to delete the default email field from the default Django User class...
class MyUser(User):
field = models.CharField(max_length = 10)
a = 'hello'
def b(self):
print 'world'
del User.email
del MyUser.email
del Myuser.field
All these give AttributeError. Deleting methods or attributes in the following way though works fine:
del MyUser.a
del MyUser.b
So I'm curious why this doesn't work; what type of things are these model fields?
Another thing I tried was overwriting email by creating an email = None in MyUser but that didn't work either (and would be slightly more ugly).
Thanks in advance!
P.s. If you are wondering why; it's more for curiousity than that it is really necessary for the application... But I think it's good not to have unused columns in database.
P.p.s. I don't want to change the Django files to manually remove 'email' from user.
EDIT: Follow-up question here (for those who want to do the same thing) Before syncdb, delete field from standard Django model

As you've discovered, model fields aren't actually class attributes, even though they appear to be.
Models are constructed by some very clever but complicated hacking around with metaclasses. When a model class definition is executed (the first time its models.py is imported), the metaclass runs through all the field definitions for that model, and calls the contribute_to_class method of each one. This ends up putting the actual fields into the new class's _meta.fields property. So the model class itself doesn't have the fields as direct properties.
Then, when a model instance is actually instantiated, Django takes that list and directly sets the new instance's attributes, using either the arguments to the constructor or the default values supplied by the fields. So, the value that's accessed via the instance has no actual field code behind it: it's a simple instance attribute.
Anyway, the upshot of all this is that to remove a field from a model definition you just need to delete it from the Model._meta.fields list.

Since Model._meta.fields is an immutable list, you won't be able to change it directly.
You can, however, modify local_fields like this:
def remove_field(model_cls, field_name):
for field in model_cls._meta.local_fields:
if field.name == field_name:
model_cls._meta.local_fields.remove(field)
remove_field(User, "email")

Related

Concise way of getting or creating an object with given field values

Suppose I have:
from django.db import models
class MyContentClass(models.Model):
content = models.TextField()
another_field = models.TextField()
x = MyContentClass(content="Hello, world!", another_field="More Info")
Is there a more concise way to perform the following logic?
existing = MyContentClass.objects.filter(content=x.content, another_field=x.another_field)
if existing:
x = existing[0]
else:
x.save()
# x now points to an object which is saved to the DB,
# either one we've just saved there or one that already existed
# with the same field values we're interested in.
Specifically:
Is there a way to query for both (all) fields without specifying
each one separately?
Is there a better idiom for either getting the old object or saving the new one? Something like get_or_create, but which accepts an object as a parameter?
Assume the code which does the saving is separate from the code which generates the initial MyContentClass instance which we need to compare to. This is typical of a case where you have a function which returns a model object without also saving it.
You could convert x to a dictionary with
x_data = x.__dict__
Then that could be passed into the object's get_or_create method.
MyContentClass.objects.get_or_create(**x_data)
The problem with this is that there are a few fields that will cause this to error out (eg the unique ID, or the _state Django modelstate field). However, if you pop() those out of the dictionary beforehand, then you'd probably be good to go :)
cleaned_dict = remove_unneeded_fields(x_data)
MyContentClass.objects.get_or_create(**cleaned_dict)
def remove_unneeded_fields(x_data):
unneeded_fields = [
'_state',
'id',
# Whatever other fields you don't want the new obj to have
# eg any field marked as 'unique'
]
for field in unneeded_fields:
del x_data[field]
return x_data
EDIT
To avoid issues associated with having to maintain a whitelist/blacklist of fields you, could do something like this:
def remove_unneeded_fields(x_data, MyObjModel):
cleaned_data = {}
for field in MyObjModel._meta.fields:
if not field.unique:
cleaned_data[field.name] = x_data[field.name]
return cleaned_Data
There would probably have to be more validation than simply checking that the field is not unique, but this might offer some flexibility when it comes to minor model field changes.
I would suggest to create a custom manager for those models and add the functions you want to do with the models (like a custom get_or_create function).
https://docs.djangoproject.com/en/1.10/topics/db/managers/#custom-managers
This would be the cleanest way and involves no hacking. :)
You can create specific managers for specific models or create a superclass with functions you want for all models.
If you just want to add a second manager with a different name, beware that it will become the default manager if you don't set the objects manager first (https://docs.djangoproject.com/en/1.10/topics/db/managers/#default-managers)

Building a list of ModelForms based on relation

I think this is best explained with some simple model code (I'm writing this from scratch so possible syntax issues - unimportant here):
class Car(models.Model)
make = models.CharField(...)
model = models.CharField(...)
class StatisticType(models.Model):
name = models.CharField(...)
class Statistic(models.Model)
car = models.ForeignKey('Car')
stype = models.ForeignKey('StatisticType')
data = models.CharField(...)
class Meta:
unique_together = (('car', 'stype'),)
We have a car with some hard-coded stats and we have some database controlled statistics. I might add Colours, Wheel Size, etc. The point is it's editable from the admin so neither I or the client need to climb through the data, but it's limited so users can only pick one of each stat (you can't define "Colours" twice).
So I'm trying to write the data input form for this now and I want a list of optional ModelForms that I can chuck on the page. I've got the simplest ModelForm possible:
class StatisticForm(forms.ModelForm):
class Meta:
model = Statistic
The tricky part (in my head) is generating an instance of this ModelForm for each StatisticType, regardless of it existing yet. That is to say if a Car object doesn't have a Colour assigned to it, the form still shows. Similarly, if it does, that instance of a Statistic is loaded in the ModelForm.
In my view, how do I generate a list of these things, regardless of there being a pre-existing instance of any given Statistic?
This seems like it should be a stupidly simple thing to do but it's late on Friday and everything looks skwonky.
Sounds like you might want to leverage an inline model formset factory.
That would allow you to create as many instances of your Statistic object as you need. If you're needing to create instances of your StatisticType on the fly, that's a bit different.
When Django instantiates forms, for a foreign key, m2m or choice field, it will only accept choices that it deems "valid", and will complain if you add a choice using JavaScript that doesn't exist in a related model or set of choices server-side.
So, if you need to make StatisticTypes on the fly, and then populate formset instances with this new value, I would suggest using Knockout.js. It's very good at keeping lots of DOM elements in sync when data changes.

Disallow non-blank values for certain model fields

As a follow up to this question I would like to know if there is way to make sure, that a model instance can not be persisted if there are any field with blank=False that have blank value. I.e. if I have this model:
class SimpleModel(models.Model):
name = models.CharField(max_length=100)
I want to disallow something like:
m = SimpleModel()
m.save()
I could do this by overriding save() and do the check manually, but maybe there is another way.
The reason for this is that I want to make really, really sure that such blank values are never inserted into the database (in case me or some of my co-coders go mad).
This is what model validation is for. You need to ensure you run m.full_clean() before save.

Django ModelForm instance with custom queryset for a specific field

I have a model not unlike the following:
class Bike(models.Model):
made_at = models.ForeignKey(Factory)
added_on = models.DateField(auto_add_now=True)
All users may work at a number of factories and therefore their user profiles all have a ManyToManyField to Factory.
Now I want to construct a ModelForm for Bike but I want the made_at list to consist of only factories at which the current user works. The idea is that users should be able to add bikes that they've assembled and enter which of the factories the bike was made at.
How do I do that?
try something like this in the view
form = BikeForm()
form.fields["made_at"].queryset = Factory.objects.filter(user__factory)
modify the Factory queryset so that it identifies the factory which the user works at.
You question might be a dupe of this.
S. Lott's answer there is the ticket to solve your problem.
He answered:
ForeignKey is represented by django.forms.ModelChoiceField, which is a ChoiceField whose choices are a model QuerySet. See the reference for ModelChoiceField.
So, provide a QuerySet to the field's queryset attribute. Depends on how your form is built. If you build an explicit form, you'll have fields named directly.
form.rate.queryset = Rate.objects.filter(company_id=the_company.id)
If you take the default ModelForm object, form.fields["rate"].queryset = ...
This is done explicitly in the view. No hacking around.
Nowaday, you should use:
form.base_fields['alumno_item'].queryset = AlumnoItem.objects.prefetch_related(
'alumno',
'alumno__estudiante',
'alumno__estudiante__profile',
'item'
)

How do you modify the default widget for all builtin form fields of a certain type in Django?

This is a follow-up on How do you change the default widget for all Django date fields in a ModelForm?.
Suppose you have a very large number of models (e.g. A-ZZZ) that is growing with the input of other developers that are beyond your control, and you want to change the way all date fields are entered (i.e. by using jQueryUI). What's the best way to ensure that all date fields are filled out using that new widget?
One suggestion from the cited question was:
def make_custom_datefield(f):
if isinstance(f, models.DateField):
# return form field with your custom widget here...
else:
return f.formfield()
class SomeForm(forms.ModelForm):
formfield_callback = make_custom_datefield
class Meta:
# normal modelform stuff here...
However, is this possible to do where you don't have explicit ModelForm's, but url patterns come from models directly? i.e. your url config is likeso:
url(r'^A/?$', 'list_detail.object_list', SomeModelA)
where SomeModelA is a model (not a form) that's turned into a ModelForm by Django in the background.
At present in my system there are no Forms for each Model. The only point of creating forms explicitly would be to add the formfield_callback suggested in the prior solution, but that goes against DRY principles, and would be error prone and labour intensive.
I've considered (as suggested in the last thread) creating my own field that has a special widget and using that instead of the builtin. It's not so labour intensive, but it could be subject to errors (nothing a good grep couldn't fix, though).
Suggestions and thoughts are appreciated.
It sounds like you want to do this project-wide (ie: you're not trying to do this in some cases, but in ALL cases in your running application).
One possibility is to replace the widget attribute of the DateField class itself. You would need to do this in some central location... something that is guaranteed to be loaded by every running instance of the django app. Middleware can help with this. Otherwise, just put it in the __init__ file of your app.
What you want to do is re-assign the widget property for the forms.DateField class itself. When a new DateField is created, Django checks to see if the code specifies any particular widget in the field property definition. If not, it uses the default for DateField. I'm assuming that if a user in your scenario really defined a particular widget, you'd want to honour that despite the change to your global API.
Try this as an example of forcing the default to some other widget... in this case a HiddenInput:
from django import forms
forms.DateField.widget = forms.HiddenInput
class Foo(forms.Form):
a = forms.DateField()
f = Foo()
print f.fields['a'].widget
# results in <django.forms.widgets.HiddenInput object at 0x16bd910>