Create custom page view counter in Django - django

I want to create my own view counter. I got inspired from django-hitcount. I read all models of this app.
In these lines:
class HitCountMixin(object):
"""
HitCountMixin provides an easy way to add a `hit_count` property to your
model that will return the related HitCount object.
"""
#property
def hit_count(self):
ctype = ContentType.objects.get_for_model(self.__class__)
hit_count, created = HitCount.objects.get_or_create(
content_type=ctype, object_pk=self.pk)
return hit_count
I couldn't understand the meaning and usage of ContentType and get_for_model(self.__class__). Can anyone help me?
Source of this mixin is here.

Since HitCountMixin can be inherited by different models in your app, HitCount model must be in some way connected to these models with a relation.
Here you can think of ContentType as a way of creating dynamic relation unlike it is with e.g. ForeignKey where you are bound to use the relation only with one model (table).
get_for_model is just Django's helper method for getting ContentType instance for given model because each model (table) would have its corresponding ContentType instance.
With example model using this mixin:
class Example(models.Model, HitCountMixin):
pass
ContentType.objects.get_for_model(self.__class__) would return ContentType instace for model Example
You can read more about ContentTypes in Django documentation

Related

How do I access my model's custom manager in a Django data migration context?

I have a custom model manager used in several of my models. This manager helps speed up DB inserts. I need to perform a data migration, and it involves migrating several millions of records/objects. I need my custom manager in my data migration. Does anyone know how to get it. In the data migration context if I run model.objects this gives me back Django's model manager.
As of now the approach I am using, and which seems to work reliably is to instantiate a local Manager for the model, then set manager's model attribute to the model I am interested in:
class MyManager(Manager):
...
def my_create_func(self):
...
class MyModel(Model):
...
objects = MyManager()
def data_migration(apps, schema_editor):
model = apps.get_model(...)
manager = MyManager()
manager.model = model
manager.my_create_func()
You can also use the Manager's use_in_migrations attribute (docs):
class MyManager(models.Manager):
use_in_migrations = True
class MyModel(models.Model):
objects = MyManager()
Why not just import your model?
from myproj.models import MyModel
MyModel.objects.filter(field=value).update(field=new_value)

create model formsets from same model but with different model forms

I have an Image model in my Django project. Because of different types of Image I have created three ModelForms according to each type:
class Xray(ModelForm):
#extra_field: Choice Field with specific options for Xray
class Meta:
model = Image
class Internal(ModelForm):
#extra_field: Choice Field with specific options for Internal
class Meta:
model = Image
class External(ModelForm):
#extra_field: Choice Field with specific options for External
class Meta:
model = Image
Each ModelForm has a save logic implemented. I want to create a model formset one for each Image type but want to use the correct ModelForm for each type of Image. I won't use this formset for editing thus I always want it to be empty and have 5 forms(5 items). I can't seem to find in django docs where i can use a specific form for a formset. Only a specific formset (inherit from BaseModelFormSet)
Is it possible to use specific form for each model_formset?
You can do the following:
from django.forms.models import modelformset_factory
from someproject.someapp.models import Image
from someproject.someapp.forms import Internal
ImageFormSet = modelformset_factory(Image, form=Internal)
Here are the docs for modelform_factory, which don't mention the form argument. However, in the "Note" below the examples therein that the function delegates to formset_factory, which is documented to take the form argument. It's just a minor docs issue, and might be a good reason to create a fork of Django, make an update to the docs, and create a pull request.

How to get the app a Django model is from?

I have a model with a generic relation:
TrackedItem --- genericrelation ---> any model
I would like to be able to generically get, from the initial model, the tracked item.
I should be able to do it on any model without modifying it.
To do that I need to get the content type and the object id. Getting the object id is easy since I have the model instance, but getting the content type is not: ContentType.object.filter requires the model (which is just content_object.__class__.__name__) and the app_label.
I have no idea of how to get in a reliable way the app in which a model is.
For now I do app = content_object.__module__.split(".")[0], but it doesn't work with django contrib apps.
The app_label is available as an attribute on the _meta attribute of any model.
from django.contrib.auth.models import User
print User._meta.app_label
# The object name is also available
print User._meta.object_name
You don't need to get the app or model just to get the contenttype - there's a handy method to do just that:
from django.contrib.contenttypes.models import ContentType
ContentType.objects.get_for_model(myobject)
Despite the name, it works for both model classes and instances.
You can get both app_label and model from your object using the built-in ContentType class:
from django.contrib.contenttypes.models import ContentType
from django.contrib.auth.models import User
user_obj = User.objects.create()
obj_content_type = ContentType.objects.get_for_model(user_obj)
print(obj_content_type.app_label)
# u'auth'
print(obj_content_type.model)
# u'user'
This is better approach respect of using the _meta properties that are defined for private purposes.

Django model manager use_for_related_fields and ModelAdmin relationships

I am having trouble getting my model manager to behave correctly when using the Admin interface. Basically, I have two models:
class Employee(models.Model):
objects = models.EmployeeManager()
username = models.CharField(max_length=45, primary_key=True)
. . .
class Eotm(models.Model): #Employee of the Month
date = models.DateField()
employee = models.ForeignKey(Employee)
. . .
And I have an EmployeeManager class that overrides the get() method, something like this:
class EmployeeManager(models.Manager):
use_for_related_fields = True
def get(self, *arguments, **keywords):
try:
return super(EmployeeManager, self).get(*arguments, **keywords)
except self.model.DoesNotExist:
#If there is no Employee matching query, try an LDAP lookup and create
#a model instance for the result, if there is one.
Basically, the idea is to have Employee objects automatically created from the information in Active Directory if they don't already exist in the database. This works well from my application code, but when I tried to create a Django admin page for the Eotm model, things weren't so nice. I replaced the default widget for ForeignKey fields with a TextInput widget so users could type a username (since username is the primary key). In theory, this should call EmployeeManager.get(username='whatever'), which would either return an Employee just like the default manager or create one and return it if one didn't already exist. The problem is, my manager is not being used.
I can't find anything in the Django documentation about using custom Manager classes and the Admin site, aside from the generic manager documentation. I did find a blog entry that talked about specifying a custom manager for ModelAdmin classes, but that doesn't really help because I don't want to change the model represented by a ModelAdmin class, but one to which it is related.
I may not be understanding what you're trying to do here, but you could use a custom Form for your Eotm model:
#admin.py
from forms import EotmAdminForm
class EotmAdmin(models.ModelAdmin):
form = EotmAdminForm
#forms.py
from django import forms
from models import Eotm, Employee
class EotmAdminForm(forms.ModelForm)
class Meta:
model = Eotm
def clean_employee(self):
username = self.cleaned_data['employee']
return Employee.get(username=username)
That, in theory, should work. I haven't tested it.

Admin Form Integration for Custom Model Fields in Django

I need a model field composed of a numeric string for a Django app I'm working on and since one doesn't exist I need to roll my own. Now I understand how "get_db_prep_value" and such work, and how to extend the Model itself (the django documentation on custom model fields is an invaluable resource.), but for the life of me I can't seem to figure out how to make the admin interface error properly based on input constraints.
How do I make the associated form field in the admin error on incorrect input?
Have a look at the Form and field validation section in the Django documentation, maybe that's what you're looking for?
You would have to make a new type of form field for your custom model field.
All you need to do is define a custom modelform which uses your new field, and then tell the admin to use that form to edit your models.
class MyModelForm(forms.ModelForm):
myfield = MyCustomField()
class Meta:
model = MyModel
class MyModelAdmin(admin.ModelAdmin):
form = MyModelForm