Django Custom User Model - Form Tuple Problems - django

I'm creating a Custom User Model, based on AbstractUser, which has several extra fields added. I've read through the documentation and assorted examples, but I'm having a couple of frustrating (and probably simple) issues
I'm adding some extra fields to the user and the admin, for later group validation. I'm trying to add the custom fields to the Custom UserCreationForm and UserChangeForm, and I'm using Class Meta: to try and add my new fields in while including the existing ones using the form
class CustomUserCreationForm(UserCreationForm):
class Meta(UserCreationForm):
model = CustomUser
fields = UserCreationForm.Meta.fields + ('character_name' ,'ritual_points', 'contribute_points',)
class CustomUserChangeForm(UserChangeForm):
class Meta(UserChangeForm):
model = CustomUser
fields = UserChangeForm.Meta.fields + ('character_name', 'ritual_points', 'contribute_points',)
Which according to all the examples I've seen should just work. However, every time i try and make the migrations, it throws a strop
TypeError: Can't convert 'tuple' object to str implicitly
Everything I've read suggests this should work without any issues.
EDIT The plot thickens. I applied an earlier answer to change the () to a list [] to convert he fields to list, and it came back with a different answer:
TypeError: Can't convert 'list' object to str implicitly
Interestingly, this is working without any problems for the CustomUserCreationForm (which gives a different error if I try and change the () to [], but CustomUserChangeForm is throwing the error.

just convert fields to list instead of tuple. This will solve your problem.
fields = UserChangeForm.Meta.fields + ['character_name', 'ritual_points', 'contribute_points',]

The problem is the UserChangeForm.Meta.fields holds the value '__all__' while UserCreationForm.Meta.fields is in fact a tuple.
>>> from django.contrib.auth.forms import UserCreationForm, UserChangeForm
>>> UserChangeForm.Meta.fields
'__all__'
>>> UserCreationForm.Meta.fields
('username',)
In the documentation it's mentioned that we also need to set the values of fieldsetsand add_fieldsets. See the note at the bottom of this paragraph in the documentation.
I solved the issues you are facing by only extending the creation form and setting the values of fieldsetsand add_fieldsets like so:
class CustomUserAdmin(UserAdmin):
add_form = CustomUserCreationForm
model = User
fieldsets = UserAdmin.fieldsets + (
(None, {'fields': ('extra_fields',)}),
)
add_fieldsets = UserAdmin.add_fieldsets + (
(None, {'fields': ('extra_fields',)}),
)
This should give you what you asked for. My theory is that due to the fact that the change form already uses '__all__' fields we don't need to alter it, only add fields to the fieldsets.

Related

Recommended way of serializing Django RawQuerySet with non-model fields

Having query like SELECT *, 'hello' AS world FROM myApp_myModel I'd like to serialize it to json.
Doesn't seem like a big deal, and there are plenty of similar questions on SO but none seems to give straight answer.
So far I've tried:
data = myModel.objects.raw(query)
# gives: ModelState is not serializable
json.dumps([dict(r.__dict__) for r in data])
# doesn't serialize 'world' column, only model fields:
serializers.serialize('json', data)
#dear God:
for r in data:
for k in dict(r.__dict__):
print(getattr(r,k))
The issue:
Builtin django core serializers are not ready to include extra fields ( from raw neither from annotation expression) It just takes model fields from _meta.local_fields.
You can see it at django django/core/serializers/base.py source code:
concrete_model = obj._meta.concrete_model #obj is an object model
...
for field in concrete_model._meta.local_fields:
if field.serialize or field is pk_parent:
if field.remote_field is None:
if (self.selected_fields is None
or field.attname in self.selected_fields):
self.handle_field(obj, field)
else:
if (self.selected_fields is None
or field.attname[:-3] in self.selected_fields):
self.handle_fk_field(obj, field)
django rest framework at rescue:
To solve your issue you can use a non builtin functionality. You can include a REST package in your project. For example django rest framework can handle extra fields:
from django.db.models import F
from aula.apps.alumnes.models import MyModel
from rest_framework.renderers import JSONRenderer
data=MyModel.objects.annotate(dummy = F('some_field') )
class MyModelSerializer(serializers.ModelSerializer):
dummy = serializers.CharField()
class Meta:
model = MyModel
fields = ('some_other_field','dummy')
read_only_fields = (
'dummy',
)
m=MyModelSerializer(data, many=True)
JSONRenderer().render(m.data)
You can create a DRF searializer for the task:
http://www.django-rest-framework.org/api-guide/serializers/
i.e.
class MyModelSerializer(serializers.ModelSerializer):
world = serializers.ReadOnlyField()
class Meta:
model = MyModel
fields = (world, ...)
you can also use serializer inheritance etc - see the docs.
There is a clean way you can do this using Django Rest Framework
First off did you know You can also execute queries containing fields that aren’t defined on the model when doing a Raw query
for example ( REF )
>>> people = Person.objects.raw('SELECT *, age(birth_date) AS age FROM myapp_person')
>>> for p in people:
... print("%s is %s." % (p.first_name, p.age))
John is 37.
Jane is 42.
That means you can use a standard serializer. You just need to tell the serializer what to do with fields that were not originally on the model consider the below. Needed to join 3 tables to a user. The user, the company they belong to and the companies membership. If your table has thousands of users and you did the standard serialiser method field, it would result in thousands of queries to get the related companies membership each time. so instead here was the solution I used
# api.py
class UserSAMAExportListApiView(ListAPIView):
serializer_class = UserExportSerializer
model = User
def get_queryset(self):
q = User.objects.raw(
"""
SELECT
[users_user].[id],
[users_user].[email],
[companies_company].[title] AS company__title,
[companies_company].[registration_number] AS company__registration_number,
[memberships_membership].number AS company__membership__number
FROM [users_user]
LEFT OUTER JOIN [dbo].[companies_company]
ON ([users_user].[company_id] = [companies_company].[id])
LEFT OUTER JOIN [memberships_membership]
ON ([companies_company].[id] = [memberships_membership].[company_id])
WHERE ([memberships_membership].[expiry_date] >= %s)
"""
, [date.today(),]
)
return q
Then just tell your standard serialiser that there are some new fields you defined
# serializers.py
class UserExportSerializer(ModelSerializer):
class Meta:
model = User
fields = (
'id',
'email',
'company__title',
'company__registration_number',
'company__membership__number',
)
def build_unknown_field(self, field_name, model_class):
"""
Return a two tuple of (cls, kwargs) to build a serializer field with. For fields that werent originally on
The model
"""
return fields.CharField, {'read_only': True}
And that's it DRF will handle the rest in a standard way and do proper serialization for you
Note you have to override the build_unknown_fields method. This is simply saying convert all the non-standard model fields to Text, if you want you can check the field name and convert to other formats here.

Django autocomplete-light error "'list' object has no attribute 'startswith'"

I've installed django-autocomplete-light and am trying to get it to work.
I have it on the (non admin) form, but when I try to actually change the value, I get a 500 error as above.
AttributeError at /autocomplete/SeriesAutocomplete/
'list' object has no attribute 'startswith'
Here are some relevant files:
autocomplete_light_registry.py:
import autocomplete_light
from models import Series
class SeriesAutocomplete(autocomplete_light.AutocompleteModelBase):
search_fields = ['^title',],
attrs = {'placeholder': 'Series name ?',}
autocomplete_light.register(Series, SeriesAutocomplete)
forms.py:
class PublicationForm(ModelForm):
series = autocomplete_light.ModelChoiceField('SeriesAutocomplete')
class Meta:
model = Publication
fields = ['title', 'series', 'dsn', 'primary_contact', 'department']
I must be missing something??
It's just a simple typo, you wrote
search_fields = ['^title',],
Which made search_fields a tuple of lists. Removing the comma should work.
search_fields = ['^title',]
Seems to be a funny bug. If you use a tuple instead of a list, i.e.
search_fields = ('^title',)
instead of
search_fields = ['^title',]
everything should work.

Django ORM access User table through multiple models

views.py
I'm creating a queryset that I want to serialize and return as JSON. The queryset looks like this:
all_objects = Program.objects.all()
test_data = serializers.serialize("json", all_objects, use_natural_keys=True)
This pulls back everything except for the 'User' model (which is linked across two models).
models.py
from django.db import models
from django.contrib.auth.models import User
class Time(models.Model):
user = models.ForeignKey(User)
...
class CostCode(models.Model):
program_name = models.TextField()
...
class Program(models.Model):
time = models.ForeignKey(Time)
program_select = models.ForeignKey(CostCode)
...
Question
My returned data has Time, Program, and CostCode information, but I'm unable to query back the 'User' table. How can I get back say the 'username' (from User Table) in the same queryset?
Note: I've changed my queryset to all_objects = Time.objects.all() and this gets User info, but then it doesn't pull in 'CostCode'. My models also have ModelManagers that return the get_by_natural_key so the relevant fields appear in my JSON.
Ultimately, I want data from all four models to appear in my serialized JSON fields, I'm just missing 'username'.
Here's a picture of how the JSON object currently appears in Firebug:
Thanks for any help!
It seems a bit heavyweight at first glance but you could look at using Django REST Framework:
http://www.django-rest-framework.org/api-guide/serializers#modelserializer
You can define and use the serializer classes without having to do anything else with the framework. The serializer returns a python dict which can then be easily dumped to JSON.
To get all fields from each related model as nested dicts you could do:
class ProgramSerializer(serializers.ModelSerializer):
class Meta:
model = Program
depth = 2
all_objects = Program.objects.all()
serializer = ProgramSerializer(all_objects, many=True)
json_str = json.dumps(serializer.data)
To customise which fields are included for each model you will need to define a ModelSerializer class for each of your models, for example to output only the username for the time.user:
class UserSerializer(serializers.ModelSerializer):
class Meta:
model = User
fields = ('username', )
class TimeSerializer(serializers.ModelSerializer):
"""
specifying the field here rather than relying on `depth` to automatically
render nested relations allows us to specify a custom serializer class
"""
user = UserSerializer()
class Meta:
model = Time
class ProgramSerializer(serializers.ModelSerializer):
time = TimeSerializer()
class Meta:
model = Program
depth = 1 # render nested CostCode with default output
all_objects = Program.objects.all()
serializer = ProgramSerializer(all_objects, many=True)
json_str = json.dumps(serializer.data)
What you really want is a "deep" serialization of objects which Django does not natively support. This is a common problem, and it is discussed in detail here: Serializing Foreign Key objects in Django. See that question for some alternatives.
Normally Django expects you to serialize the Time, CostCode, Program, and User objects separately (i.e. a separate JSON array for each) and to refer to them by IDs. The IDs can either be the numeric primary keys (PKs) or a "natural" key defined with natural_key.
You could use natural_key to return any fields you want, including user.username. Alternatively, you could define a custom serializer output whatever you want there. Either of these approaches will probably make it impossible to load the data back into a Django database, which may not be a problem for you.

Database error: no such table: auth_user. Extending AbstractUser and using model to register on admin

I'm trying to use AbstractUser to add a field to Django's standard User model. This is my code:
class GUser(AbstractUser):
uuid = UUIDField(auto=True)
This has been successful because from the shell I can say,
>>> a = GUser.objects.all()
>>> a
[<GUser: User1>]
>>> a[0].uuid
UUID('7d1f0b7b52144a2ea20db81ce74741e3')
The problem I am having is registering a new user from the /admin. When I create a new user I get a Database error:
no such table: auth_user
Before I get more into it, here is my forms.py:
class GUserChangeForm(UserChangeForm):
class Meta:
model = get_user_model()
class GUserCreationForm(UserCreationForm):
class Meta:
model = get_user_model()
def clean_username(self):
username = self.cleaned_data["username"]
try:
get_user_model().objects.get(username=username)
except get_user_model().DoesNotExist:
return username
raise forms.ValidationError(self.error_messages['duplicate'])
And my admin.py:
class GUserAdmin(UserAdmin):
form = GUserChangeForm
add_form = GUserCreationForm
fieldsets = (
(None, {'fields': [('username', 'password')]}),
('Personal info', {'fields': ('is_active','is_staff','is_superuser','groups', 'user_permissions')}),
('Important dates', {'fields': ('last_login', 'date_joined')}),
#('Universal ID', {'fields': ('uuid')})
)
admin.site.register(GUser,GUserAdmin)
I read that when this has happened to other people they implemented their own Forms (as I did) to overwrite clean_username(self):
However I don't think this is getting called when I try to add a user from the admin. The auth.forms.py file is getting called even though I register my GUserAdmin.
Also, when I delete my database (sqlite3) and then call python manage.py syncdb, I see that a auth_user table is actually not getting created.
Yes, I have included AUTH_USER_MODEL in settings.py.
Any help would be greatly appreciated.
I have read:
https://docs.djangoproject.com/en/1.5/topics/auth/customizing/#custom-users-and-the-built-in-auth-forms
Along with tons of SO posts but none of them seem to fix the issue I'm having.
Perhaps the error lies in incorrect zeroing of the database.
'Database error: no such table: str'
To delete the database, delete only database & all migrations in the migration packages(like 0001_initial.py and etc.).
Do not delete all migration packages and file "init.py". If you did this, create them. Django will not create them again.
And after, do not remember run makemigrations & migrate.
So deleted all the work I had done with this and moved on with the standard user so that I could make progress. Then one day I went back to it and managed to get it working in an hour.
I'll post the code for reference but I can't really pin point what made the difference. I do know that the creation forms and stuff were not necessary.
models.py
class PropaUser(AbstractUser):
uuid = UUIDField(auto=True)
admin.py
from django.contrib import admin
from login.models import PropaUser
admin.site.register(PropaUser)
settings.py
AUTH_USER_MODEL = "login.PropaUser"
other models.py
class Word(models.Model):
user = models.ManyToManyField(settings.AUTH_USER_MODEL)
deck = models.ManyToManyField(Deck)
word = models.CharField(max_length=100)
def __unicode__(self): # Python 3: def __str__(self):
return self.word
This is what is working. I do not know what was causing all the issues before because I think I tried this method before posting this question. This may not be a satisfactory answer but this is what is working. Earlier I was also confused about what it takes simply to add a field to the existing User. This is it.

Get model's fields in Django

Given a Django model, I'm trying to list all of its fields. I've seen some examples of doing this using the _meta model attribute, but doesn't the underscore in front of meta indicate that the _meta attribute is a private attribute and shouldn't be accessed directly? ... Because, for example, the layout of _meta could change in the future and not be a stable API?
Is _meta an exception to this rule? Is it stable and ready to use or is it considered bad practice to access it? Or is there a function or some other way to introspect the fields of a model without using the _meta attribute? Below is a list of some links showing how to do this using the _meta attribute
Any advice is much appreciated.
django object get/set field
http://www.djangofoo.com/80/get-list-model-fields
How to introspect django model fields?
_meta is private, but it's relatively stable. There are efforts to formalise it, document it and remove the underscore, which might happen before 1.3 or 1.4. I imagine effort will be made to ensure things are backwards compatible, because lots of people have been using it anyway.
If you're particularly concerned about compatibility, write a function that takes a model and returns the fields. This means if something does change in the future, you only have to change one function.
def get_model_fields(model):
return model._meta.fields
I believe this will return a list of Field objects. To get the value of each field from the instance, use getattr(instance, field.name).
Update: Django contributors are working on an API to replace the _Meta object as part of a Google Summer of Code. See:
- https://groups.google.com/forum/#!topic/django-developers/hD4roZq0wyk
- https://code.djangoproject.com/wiki/new_meta_api
I know this post is pretty old, but I just cared to tell anyone who is searching for the same thing that there is a public and official API to do this: get_fields() and get_field()
Usage:
fields = model._meta.get_fields()
my_field = model._meta.get_field('my_field')
https://docs.djangoproject.com/en/3.2/ref/models/meta/#retrieving-all-field-instances-of-a-model
get_fields() returns a tuple and each element is a Model field type, which can't be used directly as a string. So, field.name will return the field name
my_model_fields = [field.name for field in MyModel._meta.get_fields()]
The above code will return a list conatining all fields name
Example
In [11]: from django.contrib.auth.models import User
In [12]: User._meta.get_fields()
Out[12]:
(<ManyToOneRel: admin.logentry>,
<django.db.models.fields.AutoField: id>,
<django.db.models.fields.CharField: password>,
<django.db.models.fields.DateTimeField: last_login>,
<django.db.models.fields.BooleanField: is_superuser>,
<django.db.models.fields.CharField: username>,
<django.db.models.fields.CharField: first_name>,
<django.db.models.fields.CharField: last_name>,
<django.db.models.fields.EmailField: email>,
<django.db.models.fields.BooleanField: is_staff>,
<django.db.models.fields.BooleanField: is_active>,
<django.db.models.fields.DateTimeField: date_joined>,
<django.db.models.fields.related.ManyToManyField: groups>,
<django.db.models.fields.related.ManyToManyField: user_permissions>)
In [13]: [field.name for field in User._meta.get_fields()]
Out[13]:
['logentry',
'id',
'password',
'last_login',
'is_superuser',
'username',
'first_name',
'last_name',
'email',
'is_staff',
'is_active',
'date_joined',
'groups',
'user_permissions']
Now there is special method - get_fields()
>>> from django.contrib.auth.models import User
>>> User._meta.get_fields()
It accepts two parameters that can be used to control which fields are returned:
include_parents
True by default. Recursively includes fields defined on parent classes. If set to False, get_fields() will only search for fields declared directly on the current model. Fields from models that directly inherit from abstract models or proxy classes are considered to be local, not on the parent.
include_hidden
False by default. If set to True, get_fields() will include fields that are used to back other field’s functionality. This will also include any fields that have a related_name (such as ManyToManyField, or ForeignKey) that start with a “+”
This is something that is done by Django itself when building a form from a model. It is using the _meta attribute, but as Bernhard noted, it uses both _meta.fields and _meta.many_to_many. Looking at django.forms.models.fields_for_model, this is how you could do it:
opts = model._meta
for f in sorted(opts.fields + opts.many_to_many):
print '%s: %s' % (f.name, f)
fields = [f"{f.name}_id" if f.is_relation else f.name for f in model._meta.fields]
The model fields contained by _meta are listed in multiple locations as lists of the respective field objects. It may be easier to work with them as a dictionary where the keys are the field names.
In my opinion, this is most irredundant and expressive way to collect and organize the model field objects:
def get_model_fields(model):
fields = {}
options = model._meta
for field in sorted(options.concrete_fields + options.many_to_many + options.virtual_fields):
fields[field.name] = field
return fields
(See This example usage in django.forms.models.fields_for_model.)
How about this one.
fields = Model._meta.fields
If you need this for your admin site, there is also the ModelAdmin.get_fields method (docs), which returns a list of field name strings.
For example:
class MyModelAdmin(admin.ModelAdmin):
# extending change_view, just as an example
def change_view(self, request, object_id=None, form_url='', extra_context=None):
# get the model field names
field_names = self.get_fields(request)
# use the field names
...
As per the django documentation 2.2 you can use:
To get all fields: Model._meta.get_fields()
To get an individual field: Model._meta.get_field('field name')
ex. Session._meta.get_field('expire_date')
Another way is add functions to the model and when you want to override the date you can call the function.
class MyModel(models.Model):
name = models.CharField(max_length=256)
created = models.DateTimeField(auto_now_add=True)
modified = models.DateTimeField(auto_now=True)
def set_created_date(self, created_date):
field = self._meta.get_field('created')
field.auto_now_add = False
self.created = created_date
def set_modified_date(self, modified_date):
field = self._meta.get_field('modified')
field.auto_now = False
self.modified = modified_date
my_model = MyModel(name='test')
my_model.set_modified_date(new_date)
my_model.set_created_date(new_date)
my_model.save()
instance._meta.get_fields() returns a list of all the fields (i.e. columns) in a Django model.
This method is used to introspect the model's fields, their types, and their relationships with other models. The method returns a list of Field objects, which represent the individual fields in the model.
For example, suppose you have a Django model called MyModel. You can use instance._meta.get_fields() to get a list of all the fields in the model:
from myapp.models import MyModel
my_instance = MyModel.objects.get(id=1)
fields = my_instance._meta.get_fields()
The fields variable will now contain a list of all the fields in the MyModel model, including fields such as id, name, created_at, and any related fields (such as foreign keys). You can use this list to access and manipulate the individual fields in the model.