I'm working on a project using Python(3.7) and Django(3) in which I have implemented a few models. One of them is ReportsModel which has ForeignKey field to other models. Now I want to display other model data in ReportsModel admin.
Here what I have tried so far:
From models.py:
class ReportsModel(models.Model):
cdr_report = models.ForeignKey(CurrencyDistributionModel,
on_delete=models.CASCADE, null=True, default=None)
cme_report = models.ForeignKey(CurrencyManagementExpenditureModel,
on_delete=models.CASCADE, null=True, default=None)
cps_report = models.ForeignKey(CurrencyProcessingStorageModel,
on_delete=models.CASCADE, null=True, default=None)
cma_report = models.ForeignKey(CurrencyManagementAssetsModel,
on_delete=models.CASCADE, null=True, default=None)
def __str__(self):
if self.cdr_report is not None:
return self.cdr_report.RequestId
elif self.cme_report is not None:
return self.cme_report.RequestId
elif self.cps_report is not None:
return self.cps_report.RequestId
elif self.cma_report is not None:
return self.cma_report.RequestId
To display the ForeignKey field in admin I'm using the django_reverse_admin package, here how I did that:
From admin.py:
class ReportAdmin(ReverseModelAdmin):
report = None
if ReportsModel.cdr_report is not None:
report = 'cdr_report'
elif ReportsModel.cme_report is not None:
report = 'cme_report'
elif ReportsModel.cps_report is not None:
report = 'cps_report'
elif ReportsModel.cma_report is not None:
report = 'cma_report'
search_fields = ['name']
inline_reverse = [report]
inline_type = 'stacked'
admin.site.register(ReportsModel, ReportAdmin)
now in the admin, it only works for the cdr_report, when I add a report of type cme_report, I'm getting the RequestField correctly, but the cme_report field is empty.
How can I display the Inline Admin on the base of condition?
class ReportAdmin(admin.ModelAdmin):
list_display = ("id", "someVariable" )
def someVariable(self, obj):
# do your logic, query or just return FK model data
return obj.CurrencyDistributionModel.<WhateverIsYourField>
admin.site.register(ReportsModel, ReportAdmin):
Related
I am using django-import-export to import an excel to my model, what I do is that I create a form with some inputs from where it loads the file, then in form_valid() I process the file to load it to the database, the model has two foreign keys 'id_order' and 'gestion'; 'id_orden' comes in the excel and 'gestion' I get it with gestion= Gestion.objects.get(idgestion=obj.pk) which is the id of the form that I am saving, but what I want to know is how I can pass 'gestion' to ModelResource and then save it to the database
view.py
class GestionView(CreateView):
model = Gestion
form_class = GestionForm
template_name = 'asignacion/gestion.html'
success_url = reverse_lazy('asignacion_url:gestion')
def form_valid(self, form):
isvalid = super().form_valid(form)
obj = form.save()
gestion= Gestion.objects.get(idgestion=obj.pk)
file = self.request.FILES['file']
item_gestion =ItemResourceResource()
dataset = Dataset()
imported_data = dataset.load(file.read(), format='xls')
result = item_gestion.import_data(dataset, dry_run=True)
if not result.has_errors():
item_gestion.import_data(dataset, dry_run=False)
model.py
class ItemGestion(models.Model):
idgestion = models.AutoField(primary_key=True)
numero_imagenes = models.CharField(max_length=45, blank=True, null=True)
id_orden = models.ForeignKey('Asignacion', models.DO_NOTHING)
aviso_sap = models.CharField(max_length=45, blank=True, null=True)
poliza = models.CharField(max_length=45, blank=True, null=True)
observacion_cierre = models.CharField(max_length=250, blank=True, null=True)
gestion=models.ForeignKey('Gestion', models.DO_NOTHING)
resources.py
class ItemResourceResource(resources.ModelResource):
id_orden = fields.Field(column_name='id_orden', attribute='id_orden',
widget=ForeignKeyWidget(Asignacion,'id_orden'))
class Meta:
model = ItemGestion
import_id_fields = ('id_orden',)
exclude = ('idgestion', )
It is easy to do. You need to pass the gestion value into your Resource, and then link it to the instance before it is persisted:
class ItemResourceResource(ModelResource):
def __init__(self, gestion):
self.gestion = gestion
def before_save_instance(self, instance, using_transactions, dry_run):
instance.gestion = self.gestion
class Meta:
# ...
gestion = Gestion.objects.get(idgestion=obj.pk)
item_gestion = ItemResourceResource(gestion)
Obviously this means that all the instances created from the rows in your dataset will be linked to the same 'gestion' value.
btw import-export integrates with django-admin, so you can use the admin interface to import data rather than writing your own forms (if that fits your requirements). See the docs for more information.
I have been attempting to import data into my Django project using Django import-export. I have two models Ap and Job, Job has a FK relationship with Ap. Using the Admin, I can select the file and the type, CSV. So far my program seems to run, but gets hung up on the FK. I'm close, something is off and causing the import script to fail.
Models.py
class Ap(models.Model):
line_num = models.IntegerField()
vh = models.IntegerField()
vz = models.IntegerField()
status = models.CharField(
choices=statuses, default="select", max_length=40)
classified = models.BooleanField(default=False)
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
class Job(models.Model):
aplink = models.ForeignKey(Ap, related_name=(
"job2ap"), on_delete=models.CASCADE)
job_num = models.IntegerField()
description = models.CharField(max_length=200)
category = models.CharField(
choices=categories, default="select", max_length=40)
status = models.CharField(
choices=statuses, default="select", max_length=40)
dcma = models.BooleanField(default=False),
due_date = models.DateField(blank=True),
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
views.py
class ImportView(View):
def get(self, request):
form = ImportForm()
return render(request, 'importdata.html', {'form': form})
def post(self, request):
form = ImportForm(request.POST, request.FILES)
job_resource = JobResource()
data_set = Dataset()
if form.is_valid():
file = request.FILES['import_file']
imported_data = data_set.load(file.read())
result = job_resource.import_data(
data_set, dry_run=True) # Test the data import
if not result.has_errors():
job_resource.import_data(
data_set, dry_run=False) # Actually import now
else:
form = ImportForm()
return render(request, 'importdata.html', {'form': form})
resource.py
class CharRequiredWidget(widgets.CharWidget):
def clean(self, value, row=None, *args, **kwargs):
val = super().clean(value)
if val:
return val
else:
raise ValueError('this field is required')
class ForeignkeyRequiredWidget(widgets.ForeignKeyWidget):
def clean(self, value, row=None, *args, **kwargs):
if value:
print(self.field, value)
return self.get_queryset(value, row, *args, **kwargs).get(**{self.field: value})
else:
raise ValueError(self.field + " required")
class JobResource(resources.ModelResource):
aplink = fields.Field(column_name='aplink', attribute='aplink', widget=ForeignkeyRequiredWidget(Ap,'id'),
saves_null_values=False)
job_num = fields.Field(saves_null_values=False, column_name='job_num', attribute='job_num',
widget=widgets.IntegerWidget())
description = fields.Field(column_name='description', attribute='description', saves_null_values=False,
widget=CharRequiredWidget())
class Meta:
model = Job
fields = ('aplink', 'job_num', 'description',)
clean_model_instances=True
admin.py
class JobResource(resources.ModelResource):
class Meta:
model=Job
fields=('aplink','job_num','description',)
class JobAdmin(ImportExportModelAdmin):
resource_class = JobResource
admin.site.register(Job, JobAdmin)
CSV file, data to import. I have tried leaving the first column empty, as will as putting the Id of the only Ap stored in the table ie 1. I have also tried hard coding the line_num, which is 1200 the first column as well.
CSV file for importing data:
Date importing errors:
In your resources, while defining fields, you need to include id field in the list. So change JobResource to the following:
class JobResource(resources.ModelResource):
class Meta:
model = Job
fields = ('id', 'aplink', 'job_num', 'description')
If you have defined a custom id field, then you will need to provide:
import_id_fields = ('your_id_field')
I have an app that creates modelForms dynamically based on models. These forms must be dynamically loaded on the template.
I need to write a function in my view that gets all the forms from forms.py or gets a specific form by it's name. Something similar to get_models(appName) and get_model(AppName, modelName) but for forms instead of models.
How can I write it and where should I write it?
in Registery or in forms or somewhere else?
Here is my code:
Models.py
class PrimaryInfo(models.Model):
Name = models.CharField(max_length=200, blank=False, null=True) #required. Must be filled by user
Surname = models.CharField(max_length=200, blank=False, null=True)
DateOfBirth = models.DateField('date of birth', blank=False, null=True)
<Some Other fields>
...
def calculateAge(self):
if not self.DateOfBirth is None:
thisYear = timezone.now().date().year
return thisYear - self.DateOfBirth.year
pass
<some other functions>
...
#Here come all the related tables
class Jobs(models.Model):
Rel = models.ForeignKey(PrimaryInfo, on_delete=models.CASCADE)
Job = models.CharField(max_length=200)
Age = models.IntegerField(default=0)
Country = models.CharField(max_length=200)
def __str__(self):
return self.Job
<some other related models>
....
My View:
def detail(request, personId):
appName = urls.app_name
tablesPrefix = appName + '_'
person = PrimaryInfo.objects.get(pk = personId)
peopledbModels = apps.get_models(appName)
fieldsList = []
relatedModels = []
relationshipsDic = {}
formsFileName = "write.py"
parentModelForms = []
# identify which models are parent and which ones are relationships. makes a dictionary (Of string, list) for the results.
for m in peopledbModels:
if m._meta.db_table.startswith(appName):
fields = m._meta.get_fields()
for fld in fields:
if fld.is_relation:
if fld.many_to_one or fld.many_to_many or fld.one_to_one:
pass
else:
relatedModels.append(fld.name)
relationshipsDic["{}".format(m._meta.label).replace("{}.".format(appName),"")] = relatedModels
#Write the modelForm from parent model into forms.py
for pmdl in relationshipsDic.keys():
parentModelName = pmdl
modelFormExist = False
with open("{}{}/{}".format(djangoSettings.MEDIA_ROOT, appName, formsFileName)) as file:
if "class {}Form(forms.ModelForm):".format(parentModelName) in file.read():
file.close()
modelFormExist = True
if modelFormExist == False:
with open("{}{}/{}".format(djangoSettings.MEDIA_ROOT, appName, formsFileName), "a+") as file:
file.write("\n\nclass {0}Form(forms.ModelForm):\n\tclass Meta:\n\t\tmodel = {0}\n\t\tfields = '__all__'".format(parentModelName))
file.close()
parentModel = apps.get_model(appName, pmdl)
instance = get_object_or_404(parentModel, pk=personId)
parentModelForm = "{}Form".format(pmdl) #this is where I need to get the form object with a variable name from forms.py
parentModelForm = parentModelForm(instance = instance) #this is not working (string object is not callable)
parentModelForms.append(parentModelForm)
<then pass this list of models to the template>
...
My forms (automatically populated from my view):
class PrimaryInfoForm(forms.ModelForm):
class Meta:
model = PrimaryInfo
fields = '__all__'
class JobsForm(forms.ModelForm):
class Meta:
model = Jobs
fields = '__all__'
I am kind a newbie in django and python. In my app each user is assigned many projects but each project has a specific user. What I am trying to achieve is to show to a user that never created any project, a project demo.
I tried that when a user register he is directly assigned the demo project but since a project can have only one user is does not work when another sign in..
Is it possible to create an exception to a model attribute et to specify that for a specific Project can have multiple users ?
Here is my code:
Project model :
class Project(models.Model):
name = models.CharField(max_length=250)
team_id = models.ForeignKey(Team, blank=True, null=True)
project_hr_admin = models.ForeignKey('registration.MyUser', blank=True, null=True)
candidat_answers = models.ManyToManyField('survey.response')
applicant = models.ManyToManyField(MyUser, related_name="applicant")
created_at = models.DateTimeField(auto_now_add=True)
def get_absolute_url(self):
return reverse('website:ProjectDetails', kwargs={'pk1': self.pk})
Register a user :
def registerManager(request):
#import pdb; pdb.set_trace()
registered = False
if request.method == "POST":
Manager_form = ManagerForm(data=request.POST)
if Manager_form.is_valid():
user = Manager_form.save()
user.set_password(user.password)
user.is_manager = True
user.save()
registered = True
login(request, user)
demoProject = Project.objects.get(name="Project SoftScores")
request.user.project_set.add(demoProject)
return HttpResponseRedirect(reverse('website:hr_index'))
else:
print("Error!")
else:
Manager_form = ManagerForm()
return render(request, 'HR_registration_form.html',
{'Manager_form': Manager_form,
'registered': registered})
Error message:
SystemCheckError: System check identified some issues:
ERRORS:
website.DemoProject.applicant: (fields.E304) Reverse accessor for 'DemoProject.applicant' clashes with reverse ac
cessor for 'Project.applicant'.
HINT: Add or change a related_name argument to the definition for 'DemoProject.applicant' or 'Project.app
licant'.
website.DemoProject.applicant: (fields.E305) Reverse query name for 'DemoProject.applicant' clashes with reverse
query name for 'Project.applicant'.
HINT: Add or change a related_name argument to the definition for 'DemoProject.applicant' or 'Project.app
licant'.
website.Project.applicant: (fields.E304) Reverse accessor for 'Project.applicant' clashes with reverse accessor f
or 'DemoProject.applicant'.
HINT: Add or change a related_name argument to the definition for 'Project.applicant' or 'DemoProject.app
licant'.
website.Project.applicant: (fields.E305) Reverse query name for 'Project.applicant' clashes with reverse query na
me for 'DemoProject.applicant'.
HINT: Add or change a related_name argument to the definition for 'Project.applicant' or 'DemoProject.app
licant'.
WARNINGS:
website.DemoProject.project_hr_admin: (fields.W340) null has no effect on ManyToManyField.
Edited Code:
class BaseProject(models.Model):
name = models.CharField(max_length=250)
team_id = models.ForeignKey(Team, blank=True, null=True)
project_hr_admin = models.ForeignKey('registration.MyUser', blank=True, null=True)
candidat_answers = models.ManyToManyField('survey.response')
applicant = models.ManyToManyField(MyUser, related_name="applicant")
created_at = models.DateTimeField(auto_now_add=True)
class Meta:
abstract = True
def get_absolute_url(self):
return reverse('website:ProjectDetails', kwargs={'pk1': self.pk})
def __str__(self):
return self.name
class Project(BaseProject):
def has_member_responses(self, result=None):
try:
x = Project.objects.get(id=self.id).team_id.members.all()
for i in x:
result = 1
if i.response_set.exists():
result = result * True
else:
result = result * False
return result
except AttributeError:
return False
class DemoProject(BaseProject):
project_hr_admin = models.ManyToManyField('registration.MyUser', blank=True, null=True)
There are several ways you can handle this, but the cleanest In my opinion is to create an abstract BaseProject model and make that the parent class of the Project model and a DemoProject model.
class BaseProject(models.Model):
name = models.CharField(max_length=250)
team_id = models.ForeignKey(Team, blank=True, null=True)
project_hr_admin = models.ForeignKey('registration.MyUser', blank=True, null=True)
candidat_answers = models.ManyToManyField('survey.response')
applicant = models.ManyToManyField(MyUser, related_name="applicant")
created_at = models.DateTimeField(auto_now_add=True)
class Meta:
abstract = True
def get_absolute_url(self):
return reverse('website:ProjectDetails', kwargs={'pk1': self.pk})
Now create the DemoProject as a child of BaseProject
class DemoProject(BaseProject):
project_hr_admin = models.ManyToManyField('registration.MyUser', blank=True, null=True)
Then you can update your registration code to use the new model.
def registerManager(request):
registered = False
if request.method == "POST":
Manager_form = ManagerForm(data=request.POST)
if Manager_form.is_valid():
user = Manager_form.save()
user.set_password(user.password)
user.is_manager = True
user.save()
registered = True
login(request, user)
demo_project = DemoProject.objects.get(name="Demo Project")
request.user.project_set.add(demoProject)
return HttpResponseRedirect(reverse('website:hr_index'))
else:
print("Error!")
else:
Manager_form = ManagerForm()
return render(request, 'HR_registration_form.html',
{'Manager_form': Manager_form,
'registered': registered})
This way, you've seperated the two entities and managed to preserve the behaviour/identitiy of the DemoProject (A DemoProject is still a Project).
This also means you can modify the main Project model without affecting the DemoProject model.
Your main Project model should look like this now
class Project(BaseProject):
# any custom stuff that's unique to a project
pass
It's a sort of cms type application
I have an article model and some specializations in models.py
class Article(models.Model):
foo = models.CharField(max_length=50)
bar = models.CharField(max_length=100,blank=True)
DISPLAY_CHOICES = (
('N', 'None'),
('C','Carousel'),
('M','Marketing'),
('F','Featurette')
)
display = models.CharField(max_length=1, choices = DISPLAY_CHOICES)
def __unicode__(self):
return self.title
class Artist(Article):
website = models.URLField(max_length=200,blank=True)
class Venue(Article):
location = models.CharField(max_length=150)
map_link = models.URLField(max_length=200,blank=True)
class Event(Article):
time = models.DateTimeField()
venue = models.ForeignKey(Venue)
performers = models.ManyToManyField(Artist)
I want to render these in different ways depending on the value of article.display but when I call
articles.objects.all()
I still need the extra attributes form the subclasses so I wrote
#views.py
def castToSubClass(article):
try:
return Artist.objects.get(article_ptr_id = article.id)
except:
try:
return Event.objects.get(article_ptr_id = article.id)
except:
try:
return Venue.objects.get(article_ptr_id = article.id)
except:
return article
def index(request):
carousel = [castToSubClass(article) for article in Article.objects.filter(display='C']
marketing = [castToSubClass(article) for article in Article.objects.filter(display='M'[:3]]
featurettes = [castToSubClass(article) for article in Article.objects.filter(display='F']
return render_to_response('frontpage.html',
{
'carousel': carousel,
'marketing':marketing,
'featurettes': featurettes
})
to turn them all in the appropriate subclass object, this apart from seeming clunky seems to mean I'm hitting the database twice for every (or nearly every) item in the queryset.
Is there a way to do this in the initial calls to the manager instead?
Thanks.
Use one model to store everything, and add a field to distinguish the article type, so that you can render different look for every type combine with display in the template(Like tumblr do).
class Article(models.Model):
foo = models.CharField(max_length=50)
bar = models.CharField(max_length=100,blank=True)
DISPLAY_CHOICES = (
('N', 'None'),
('C','Carousel'),
('M','Marketing'),
('F','Featurette')
)
display = models.CharField(max_length=1, choices = DISPLAY_CHOICES)
ARTICLE_TYPE_CHOICES = (
('artist', 'Artist'),
('venue', 'Venue'),
('event', 'Event'),
)
type = models.CharField(max_length=32, choices = ARTICLE_TYPE_CHOICES)
website = models.URLField(max_length=200,blank=True, null=True)
location = models.CharField(max_length=150, blank=True, null=True)
map_link = models.URLField(max_length=200,blank=True, null=True)
time = models.DateTimeField(null=True)
venue = models.ForeignKey('self', null=True)
performers = models.ManyToManyField('self', null=True)
def __unicode__(self):
return self.title
#views.py
def index(request):
carousel = Article.objects.filter(display='C')
marketing = Article.objects.filter(display='M')
featurettes = Article.objects.filter(display='F')
return render_to_response('frontpage.html',{'carousel': carousel, 'marketing':marketing, 'featurettes': featurettes})