Django - How to check all the attributes of a queryset? - django

I am looking a way to get all the attributes of a variable after we set the value in it using queryset.
For example...refer below code... using user.id or user.first_name i can get the value for that attribute. But if i want to check what all other attributes it has? Is there a way i can get.
If we use user then it will just return which is what we have defined in admin.py.
Code, i am using Django Shell
from django.contrib.auth.models import User
user=User.objects.get(id=1)
user.first_name # Will return some value say testUser
user.id # will return some value say 1

I guessing what you are saying is you want to print all attributes of an object instead of QuerySet
To print all attributes of an object you can do the follow:
from django.contrib.auth.models import User
user=User.objects.get(id=1)
print(user.__dict__)
But if you just what to find out what django default user models fields are, you can check this docs:
https://docs.djangoproject.com/en/3.0/ref/contrib/auth/

Django returns a tuple of fields associated with a model if you want.
Django 3.0:
from django.contrib.auth.models import User
User._meta.get_fields()

Adding to #MarkL 's answer:
You can pretty print it for better readability.
from django.contrib.auth.models import User
from pprint import pprint # new
user=User.objects.get(id=1)
pprint(user.__dict__) # new ----> pprint() instead of print()
Another way which I always prefer using over __dict__ is vars() method:
user=User.objects.get(id=1)
pprint(vars(user))
Both return the same result but to me vars() is more convenient to write than the dunder dict method i-e __dict__, coz I am too lazy to write 4 underscores.

Building on MarkL's answer, here is the same thing, but with nicer formatting:
[f"{key}: {value}" for key, value in user.__dict__.items()]
(Sorry, I don't have enough rep to post this as a comment.)

Related

Add UID or Unique ID in Wagtail/Django

So my question was how I can generate a random UID or slug for my CMS. If I use the default id which is coming from API v2 people can easily guess my next post URL easily.
Is there any way to add a unique slug/ID/UUID for my wagtail CMS?
Here is the simple solution, go to your bolg/models.py and first install pip install django-autoslug
Then import this
from django.db.models import CharField, Model
from autoslug import AutoSlugField
from django.utils.crypto import get_random_string
Here we are adding another extension called get_random_string which will generate a random string every time you call it.
Then add this in your AddStory {Your add post class}
#Defining a function to get random id every call
def randomid(self):
return(get_random_string(length=10))
# creating a custom slug to show in frontend.
news_slug = AutoSlugField(populate_from='randomid', unique = True, null= True, default=None)
Here I defined a function called randomid which will return a 10 digit string on every call. Then I created a new field called news_slug which is coming from Django auto_slug extension, wich will populate from the randomid, and the URL must unique (ex: if it all 10 digit string are finished it will add -1,-2 so on ( ex: sxcfsf12e4-1), here null = true means that this field can be empty so that autoslug can generate unique ID.
Then expose that news_slug filed in API.
api_fields=[
APIField("news_slug"),
]
you can access all field like this /api/v2/pages/?type=blog.AddStory&fields=*
Here type=blog is your blog app and AddStory is your class.
Hope this helps, it took time for me to find out. More wagtail tutorials will come.
A variant on the answer that I use for user ID's:
import random
import string
from django_extensions.db.fields import AutoSlugField
....
class CustomUser(AbstractUser):
....
uuid = AutoSlugField(unique=True)
....
def slugify_function(self, content):
return ''.join((random.choice(string.ascii_letters + string.digits) for i in range(12)))
AutoSlugField is part of django_extensions
AutoSlugField has a built in slugify_function to generate the slug, you can override that just by declaring your own in the class
This slugify_function will generate a random 12 alpha-numeric character string including upper/lower case. Permutations are (I think) 1 e21 so chances of guessing are extremely slim.

Django custom validation before the data is saved (Enforce at the database level)

This is an extension from my post here preventing crud operations on django model
A short into to the problem , im currently using a package called django-river to implement a workflow system in my application. The issue is that they do not have a predefined 'start' , 'dropped' , 'completed' state. Their states are stored as a django model instance. This would mean that my application is unable to programmatically differentiate between the states. Therefore , the labels of these states has to be hardcoded into my program (Or does it? Maybe someone has a solution to this?)
Suppose that there is no solution to the issue other than hardcoding the states into my application , this would mean that i would have to prevent users from updating , or deleting these states that i have pre created initially.
My idea is to have a form of validation check within the django model's save method . This check would check that the first 3 instances of the State model is always start , deactivated and completed and in the same order. This would prevent the check from passing through whenever a user trys to change items at the ORM level.
However , it would seem that there is 2 issues with this:
I believe django admin doesn't run the model class save method
Someone is still able to change the states as long as the way they changed it does not pass through the save() method. AKA from the DB SQL commands
Although it is unlikely to happen , changing the name would 'break' my application and therefore i wish to be very sure that no one can edit and change these 3 predefined states.
Is there a fool proof way to do this?
My idea is to have a form of validation check within the django model's save method.
if i understand your description, maybe you can just override the save() function of your model like so:
class MyModel(models.Model):
[..]
def save(self, *args, **kwargs):
# Put your logic here ..
super(MyModel, self).save(*args, **kwargs)
I got the answer from django documentation
from django.core.exceptions import ValidationError
from django.utils.translation import gettext_lazy as _
def validate_even(value):
if value % 2 != 0:
raise ValidationError(
_('%(value)s is not an even number'),
params={'value': value},
)
You can add this to a model field via the field’s validators argument:
from django.db import models
class MyModel(models.Model):
even_field = models.IntegerField(validators=[validate_even])
FYI: It is not really mandatory to use gettext_lazy and you can use just message as follows
from django.core.exceptions import ValidationError
def validate_even(value):
if value % 2 != 0:
raise ValidationError(
('%(value)s is not an even number'),
params={'value': value},
)

how to get the value of an attribute (pk) using QuerySet

I am trying to get the value of the primary attribute (pk). How to do it ?
Equivalence for this
SELECT id FROM User WHERE username="Fokoa"
Multiple ways:
User.objects.filter(username='Fokoa').values_list('id', flat=True)
Or, if you know that username is unique:
user = User.objects.get(username='Fokoa')
user.id
You can use a .values_list(..) [Django-doc] for that:
from django.contrib.auth.models import User
User.objects.filter(username='Fokoa').values_list('pk', flat=True)
or we can conver this to a list of primary keys with:
from django.contrib.auth.models import User
list(User.objects.filter(username='Fokoa').values_list('id', flat=True))
That being said, it is not very common to query for a specific column in Django. It is good practice to see primary keys as "black box tokens", so not interpret these as integers process these. After all, summing up two primary keys frequently does not make much sense.

get_model() vs from .models import somemodelname

What is/are the best practices to use get_model() and when should it be imported ?
Ref: https://docs.djangoproject.com/en/1.8/ref/applications/
You usually use get_model() when you need to dynamically get a model class.
A practical example: when writing a RunPython operation for a migration, you get the app registry as one of the args, and you use apps.get_model('TheModel') to import historical models.
Another example: you have an app which has dynamically built serializers and you set their Meta.model to the class you just got with get_model() .
Yet another example is importing models in AppConfig.ready() with self.get_model().
An important thing to remember, if you are using AppConfig.get_model() or apps.get_models(), that they can be used only once the application registry is fully populated.
The other option (from .models import TheModel) is just the default way to import models anywhere in your code.
These are just examples though, there are many other possible scenarios.
I Prefer, use .models import, cause is a simple way to get the Model Object.
But if you works with metaclasses, maybe the get_model, would be the best option.
def get_model(self, app_label, model_name=None):
"""
Returns the model matching the given app_label and model_name.
As a shortcut, this function also accepts a single argument in the
form <app_label>.<model_name>.
model_name is case-insensitive.
Raises LookupError if no application exists with this label, or no
model exists with this name in the application. Raises ValueError if
called with a single argument that doesn't contain exactly one dot.
"""
self.check_models_ready()
if model_name is None:
app_label, model_name = app_label.split('.')
return self.get_app_config(app_label).get_model(model_name.lower())
Maybe this SO POST, can help too.

Django - Custom Model Method - How to specify datatype so Admin formats it properly?

Example:
class MyModel(models.Model):
field1=models.CharField(..)
field2=models.DateTimeField()
def today(self):
return self.field2
When I look at this in the admin site, field2 is formatted differently than the today field.
How can I tell the admin site to treat today like it's treating field2? I.e., tell Django admin that 'today' is a models.DateTimeField?
Here is what it's showing:
Field2 today
April 5, 2011, 9:10 a.m. 2011-04-11 08:47:27
To obtain DateTime object call datetime.datetime.now() instead of datetime.datetime.today()
EDIT:
Or use models.DateField() for field2 instead of models.DateTimeField() :-)
EDIT2:
Here is the solution:
def today(self):
from django.utils import formats
return formats.localize(self.field2)
That's some really really weird behaviour. At a total guess, it may have something to do with django settings; specifically the DATETIME_FORMAT (and related) settings. The framework probably does introspection on fields, and if they are of DateTime type, are rendered according to the aforementioned settings.
Introspection on methods wouldn't make sense in the majority of cases, so I could understand this behaviour if it is the case.
Try modifying the settings accordingly (provide different datetime formats), and see if the fields change and the method remains the same.
Edit:
Looking at django.contrib.databrowse.datastructures, there is a section of code that does something like:
if isinstance(self.field, models.DateTimeField):
objs = capfirst(formats.date_format(self.raw_value, 'DATETIME_FORMAT'))
I'd imagine a similar thing happening within the admin app, though I can't find an exact reference at the moment.
To achieve what you want, you'll need to format your datetime appropriately:
def today(self):
from django.conf import settings
return self.field2.strftime(settings.DATETIME_FORMAT)
Or, using #cata's comment:
def today(self):
from django.utils.formats import localize
return localize(self.field2)
If you choose to supply a "list_display" item through your own function, and you're not happy with the default output, you'll need to format it yourself. In this case, if you want to have identical formatting to what the DateTime database field ends up with:
from django.utils import formats
def today(self):
return formats.localize(self.field2)
Background:
templates/admin/change_list.html
uses the template tag
django.contrib.admin.templatetags.admin_list.result_list
which in turn will call
django.contrib.admin.templatetags.admin_list.items_for_result()
to render the individual column values for each row.
You'll see that both your values start off being a DateTime instance, either through database lookup or calling your function, see
django.contrib.admin.util.lookup_field()
but the return value "f" will only be a field if there was a database field. You provided a function, so lookup_field() will only provide the value, and "f" will be None.
So in items_for_result(), your value will run through the "if f is None" block and miss out on
result_repr = display_for_field(value, f)
In other words,
django.contrib.admin.util.display_for_field()
will only be called on the database value to format according to the field type, so this is the treatment your function value is missing out on:
elif isinstance(field, models.DateField) or isinstance(field, models.TimeField):
return formats.localize(value)
and you'll need to do that last line yourself, as shown above.
EDIT: Regarding your question
How can I tell the admin site to treat
today like it's treating field2? I.e.,
tell Django admin that 'today' is a
models.DateTimeField?
It's not a models.DateTimeField, it's a function value. If it were a models.DateTimeField, it would be describing your model. Look at all the stuff that entails: http://docs.djangoproject.com/en/dev/ref/models/fields/
In your example, you really could just use field2. Apparently you want to do things to its value, calculate it etc. - so what's today.db_column then?
That said, it would be nice if function values that are DateTime instances were run through format.localize() by default, as that's what the documentation on localization seems to be promising.
By the way, I would rather define a formatted value in the ModelAdmin than in the model itself. I usually call it something like "formatted_today" (to keep the datetime value of the original today()), it's just that if the Admin is the only place that needs the formatted value, imho that's where it should be defined.
All previous answers provide solutions, that will handle timezone info incorrectly in new Django versions.
field2 and today will generally show different time if settings.USE_TZ==True.
I found this question today and have spent some time to figure out the correct way:
from django.db import models
from django.contrib import admin
from django.utils.timezone import template_localtime
from django.utils.formats import localize
class MyModel(models.Model):
# ...
field2=models.DateTimeField()
class MyModelAdmin(admin.ModelAdmin):
# ...
def today(self, obj):
return localize(template_localtime(obj.field2))
admin.site.register(MyModel, MyModelAdmin)