django: use a string to select a specifc model - django

What I want to do be able to use the name of a model as an input to a function so the objects methods can be performed against the specified model. For example:
from app.models import model1, model2
def select_all_from_model(model_name):
all = model_name.objects.all()
return all
all = select_all_from_model('model1')
all = model_name.objects.all() is a nonsense line. I need to have a model object, not a string name, for the objects.all() select to work. How do I load the model object based on the string value of model_name?

You can use models.get_model() function:
>>> model_class = models.get_model('App', 'Model1')
>>> model_class.objects.all()
>>> [...]
That's if you need to use a string. But you can pass classes around in python. Here's an example:
>>> from app.models import Model1
>>> select_all_from_model(Model1)
>>> [...]

Related

Django DateTimeRangeField: default=[timezone.now()]-[timezone.now()]+[10YEARS]

I want an "active_in" attribute as a timeframe. I assume that the DBMS is optimized for the postgresql tsrange field, and as such it is preferable to utilize the DateTimeRangeField rather than 2 separate fields for start_date and end_date.
Doing this I desire a default value for the field.
active_in = models.DateTimeRangeField(default=timezone.now+'-'+timezone.now+10YEARS)
Is my assumption about the DateTimeRangeField performance true?
Is there a smart solution be it creating a new; function,class or
simply manipulating the 2nd last digit?
My possible solutions:
Code using string manipulation:
active_in = models.DateTimeRangeField(default=timezone.now+'-'+timezone.now[:-2]+'30')
Code using custom function object: (adjusted from here: https://stackoverflow.com/a/27491426/7458018)
def today_years_ahead():
return timezone.now + '-' timezone.now() + timezone.timedelta(years=10)
class MyModel(models.Model):
...
active_in = models.DateTimeRangeField(default=today_years_ahead)
There's no need for string manipulation, as the documented Python type for this field is DateTimeTZRange.
I can't say I've ever used this field before, but something like this should work:
from psycopg2.extras import DateTimeTZRange
from django.utils import timezone
from datetime import timedelta
def next_ten_years():
now = timezone.now()
# use a more accurate version of "10 years" if you need it
return DateTimeTZRange(now, now + timedelta(days=3652))
class MyModel(models.Model):
...
active_in = models.DateTimeRangeField(default=next_ten_years)

django forms get field output in view

I have model form with several fields works as expected. Now I need, for specific reasons, to get form field in view but got error 'EditPostForm' object has no attribute 'about' when I call mydata1 = form.about in view. But about field exist of course. form.data.about also wont work etc. So how can I get it? Thanks.
If you form has instance associated to it, you can try
post = EditPost.objects.get(id=id)
form1 = EditPostForm(instance=post)
form1.instance.about
Based on your comment below if you are using ManyToMany relation you can get the value as
>>> bf = BookForm(instance=book)
>>> bf.instance.authors
<django.db.models.fields.related.ManyRelatedManager object at 0x0000000004658B38>
>>> bf.instance.authors.all() #which returns a query set of related objects
[<Author: Kotian>]
>>> bf.instance.authors.all()[0]
<Author: Kotian>
>>> bf.instance.authors.all()[0].name
u'Kotian'
or based on how you have defined the ManyToMany
>>> af = AuthorForm(instance=author)
>>> af.instance.name
u'MyName'
>>> af.instance.book_set
<django.db.models.fields.related.ManyRelatedManager object at 0x0000000004658C18>
>>> af.instance.book_set.all() # returns queryset
[<Book: Book object>, <Book: Book object>]
>>> af.instance.book_set.all()[0] #accessing first object here
<Book: Book object>
>>> af.instance.book_set.all()[0].name
u'Lepord'

In django how can i create a model instance from a string value?

All,
I have strings that represent my model and fields, like this
modelNameStr = 'MyModel'
fieldNameStr = 'modelField'
My model looks like this;
class MyModel(models.Model):
modelField = ForeignKey( ForeignModel )
...
What i want to do is create an instance of MyModel using the string variables, something like
model_instance = modelNameStr.objects.filter(fieldNameStr=ForeignModelInstance)
How can i do this?
Gath
model_instance = ContentType.objects.get(app_label=u'someapp', model=modelNameStr).model_class()(**{fieldNameStr: ForeignModelInstance})
Phew! Try saying that five times fast! But make sure you use the appropriate value for app_label.
Retrieving the model class you can use the get_model function from Django. Though you have to use a string like my_app.MyModel where 'my_app' is your django app which includes the model. Filtering field values can be achieved via a dict. Here an example:
from django.db.models import get_model
modelNameStr = 'my_app.MyModel'
fieldNameStr = 'modelField'
ModelClass = get_model(*model_class.split('.'))
filters = {fieldNameStr: ForeignModelInstance}
model_instance = ModelClass.objects.filter(**filters)

Is it possible to specify a QuerySet model dynamically as a string?

I am trying to build a query in Django dynamically. I have a lot of models that I would like to build a query for, but I don't want to code the name of the model, I want to pass it as a string.
from django.db.models.query import QuerySet
a_works = QuerySet(model_A)
a_doesnt_work = QuerySet("model_A") # I want this to work, too
a_works.filter(pk=23) # no error
a_doesnt_work.filter(pk=23) # error: AttributeError: 'str' object has no attribute '_meta'
# then I am dynamically filtering different fields, which works fine with a_works
kwargs = { "%s__%s" % (field, oper) : val }
results = a_works.filter( **kwargs )
Is there a way to make the dynamic model selection work?
Don't try and build querysets via the QuerySet class itself. You should always go via a model's Manager.
You can get the model via the get_model function defined in django.db.models. It takes parameters of the app name and the model name.
from django.db.models import get_model
model = get_model('myapp', 'modelA')
model.objects.filter(**kwargs)
Refer this: https://stackoverflow.com/a/75168880/7212249
from django.apps import apps
def get_app_label_and_model_name(instance: object):
"""
get_model(), which takes two pieces of information — an “app label” and “model name” — and returns the model
which matches them.
#return: None / Model
"""
app_label = instance._meta.app_label
model_name = instance.__class__.__name__
model = apps.get_model(app_label, model_name)
return model
How to use?
model_name = get_app_label_and_model_name(pass_model_object_here)
and use this to get dynamic model name for queries
model_name = get_app_label_and_model_name(pass_model_object_here)
query_set = model_name.objects.filter() # or anything else

How do I convert a django QuerySet to numpy record array?

How do I convert a django QuerySet to numpy record array?
PS: I know you can iterate and construct it and but is there any other cleaner solution?
import numpy as np
qs = MyModel.objects.all()
vlqs = qs.values_list()
r = np.core.records.fromrecords(vlqs, names=[f.name for f in MyModel._meta.fields])
This uses the QuerySet iterator directly and avoids the time-and-garbage-wasting step of creating a python list. It also uses MyModel._meta.fields to get the actual field names from the model, as explained at Get model's fields in Django
If you just want a single field (e.g. the 'votes' field of the model) extracted into a one-dimensional array, you can do:
vlqs = qs.values_list('votes', flat=True)
votes = np.fromiter(vlqs, numpy.dtype('int_'))
This is like asking "how do I convert the contents of my fridge into dinner?". It depends on what you have in your fridge and what you'd like to eat. The short answer (equivalent to saying "by cooking") is to iterate over the queryset, constructing objects of whatever composite data types you'd like to instantiate the array with (generally an iterable and a dictionary). The long answer depends on what you'd actually like to accomplish.
If you want to get all of your objects and create a numpy array with objects as elements of array:
import numpy as np
qs = MyModel.objects.all()
numpy_array = np.array(list(qs))
According to my work, I use something as below:
import numpy as np
qs = MyModel.objects.values_list('id','first_name','last_name').filter(gender='male').order_by('id')
numpy_array = np.array(list(qs))
Rows of array corresponds to records and columns of array corresponds to values that I defined above (id, first name, last name).
What I was looking for:
From QuerySet qs get vlqs (django.db.models.query.ValuesListQuerySet)
vlqs = qs.values_list()
Covert vlqs to list
mylist = list(vlqs)
Create numpy record array
# Names are the model fields
r = np.core.records.array(mylist, names='field1, field2, field3')
And to put it into a neat little function to which you just pass any Django Queryset:
import pandas as pd
import numpy as np
def qs_to_df(qs):
""" QuerySet to DataFrame """
Model = qs.model
np_array = np.core.records.fromrecords(qs.values_list(), names=[f.name for f in Model._meta.fields])
return pd.DataFrame(np_array)
what you could do is:
[index[0] for index in qs.values_list('votes')]
and ready...XD
Going off of #CpILL 's answer you can turn most querysets into a numpy record array like so:
def qs_to_ra(qs, *args):
"""
Turn most querysets directly into a numpy record array
:param qs: django queryset
:param args: takes a list of field names to specify
:return: numpy.recarray
"""
model = qs.model
if args:
return np.core.records.fromrecords(qs.values_list(*args), names=args)
return np.core.records.fromrecords(qs.values_list(), names=[f.name for f in model._meta.fields])
You can also turn them directly into a pandas dataframe like so:
def qs_to_df(qs, *args):
"""
Turn most querysets directly into a pandas dataframe.
:param qs: django queryset
:param args: takes a list of field names to specify
:return: pandas.DataFrame
"""
model = qs.model
if args:
return pd.DataFrame.from_records(list(qs.values_list(*args)), columns=args)
return pd.DataFrame.from_records(list(qs.values_list()), columns=[f.name for f in model._meta.fields])