How can I get the Id after the row have been imported in the Django Admin with Django-import-export? - django-admin

Currently, I can access some data before or after to save the instance in the DB. But I couldn't get the ID after the row was import
class OtherResource(resources.ModelResource):
"""
My code
"""
def after_import_row(self, row, row_result, **kwargs):
my_class= MyClass.objects.get(id=row['id'])
my_class.first_name = 'some new'
recipient.save()
class MyClassAdmin(ImportMixin, admin.ModelAdmin):
resource_class = OtherResource
def import_data(self, dataset, dry_run=False, raise_errors=False, use_transactions=None, collect_failed_rows=False, **kwargs):
"""
Getting the user's request to pass it to the import
"""
result = OtherResource.import_data(dataset, dry_run=False, raise_errors=False, use_transactions=None, user=self.request.user)
return result
Dango says the query doesn't exist, because I couldn't get the id

Related

How do you get the instance of the imported object after importing with django-import-export?

I'm trying to add objects to a related model after importing using django-import-export.
My Models
class Event(models.Model):
title = models.CharField(max_length=150)
class EventSession(models.Model):
event = models.ForeignKey(Event, on_delete=models.CASCADE, related_name="sessions")
start_date = models.DateTimeField()
end_date = models.DateTimeField()
My Import Data
id
start_date
start_time
minutes
sessions
9/1/21
10:00
60
2
9/8/21
10:00
60
3
My ModelResource Class
I am trying to override after_import_row to get the imported event and then create
the event sessions, but I don't know how to get the imported event.
class EventResource(resources.ModelResource):
start_date = fields.Field()
start_time = fields.Field()
def after_import_row(self, row, row_result, row_number=None, **kwargs):
""" After importing the row, get the saved event and save the event sessions."""
event = get_instance() # HOW TO GET THE INSTANCE????
start_datetime = timezone.make_aware(
datetime.combine(row["start_date"], row["start_time"])
)
end_datetime = start_datetime + timedelta(minutes=row["minutes"])
first_session = EventSession(
event=event,
start_date=start_datetime,
end_date=end_datetime,
)
first_session.save()
for _ in range(1, row["sessions"]):
# Sessions are always consecutive days
start_datetime = start_datetime + timedelta(days=1)
end_datetime = end_datetime + timedelta(days=1)
event_session = EventSession(
event=event,
start_date=start_datetime,
end_date=end_datetime,
)
event_session.save()
return super().after_import_row(
row, row_result, row_number=row_number, **kwargs
)
class Meta:
model = Event
fields = (
"id",
"title",
"start_date",
"start_time",
"sessions",
)
skip_unchanged = True
report_skipped = True
I figured out a solution. I can save the instance as an attribute of the EventResource object by overriding after_save_instance().
def after_save_instance(self, instance, using_transactions, dry_run):
self.instance = instance
return super().after_save_instance(instance, using_transactions, dry_run)
Then in after_import_row(), I use:
event = self.instance
You can also get hold of the instance id as follows:
def after_import_row(self, row, row_result, row_number=None, **kwargs):
print(row_result.object_id)
Obviously you can then load the object if required.
An alternative is to override after_import(). In this case, the result object passed to the method contains rows, which is a list of all imported rows.
def after_import(self, dataset, result, using_transactions, dry_run, **kwargs):
for row_result in result:
print(row_result.object_id)

how to initialize inline form set for admin page

I'm trying to initialize an inline formset of a django 1.8 admin page when a new BookStore object is created, a number of default books will be already listed:
from django.forms import formset_factory
class BookInlineAdmin(admin.TabularInline):
model = Book
form = BookForm
extra = 1
def get_formset(self, request, obj=None, **kwargs):
formset = super(BookInlineAdmin, self).get_formset(request, obj, **kwargs)
BookFormSet = formset_factory(BookForm, extra=4)
formset = BookFormSet(initial=[
{'stype': 'comic', 'name': 'abc'},
{'stype': 'drama', 'name': 'aaa'}])
return formset
However, it is failing:
TypeError: 'BookFormFormSet' object is not callable
How can I initialize the inline form set for admin page?

Getting a second parameter based on the first parameter in Jenkins

I have a task where my Jenkins job needs two parameters for build. The first specifies the application name and can be either QA, Dev, Prod etc and the second is a server which is dependent on the first one.
Example: If I chose the app name as QA, the second parameter should display values like QAServer1, QAServer2, QAServer3.
I'm using Active Choices Plugin (https://wiki.jenkins.io/display/JENKINS/Active+Choices+Plugin) to get this done but facing an problem in fetching the second parameter contents.
Snapshots:
For obtaining the second parameter, I've written a Groovy code which reads the respective files of the selected first parameter and gets the details.
code:
#!/usr/bin/env groovy
import hudson.model.*
def Appliname = System.getenv("APPNAME")
//println Appliname
def list1 = []
def directoryName = "C:/Users/Dev/Desktop/JSONSTest"
def fileSubStr = Appliname
def filePattern = ~/${fileSubStr}/
def directory = new File(directoryName)
def findFilenameClosure =
{
if (filePattern.matcher(it.name).find())
{
def jsoname = it.name
def jsoname1 = jsoname.reverse().take(9).reverse()
list1.add(jsoname1.substring(1,4))
String listAsString = "[\'${list1.join("', '")}\']"
println "return"+listAsString
}
}
directory.eachFileRecurse(findFilenameClosure)
The above code will print the output as return['QAServer1', 'QAServer2'] which i want to use it as input for the second parameter.
Snapshot of Second parameter:
Somehow the Groovy script is not being executed and second parameter value remains empty. How can i get this done dynamically. Am i following the right away to it. Kindly help me figure out. TIA
Would you like to try below change
From:
def findFilenameClosure =
{
if (filePattern.matcher(it.name).find())
{
def jsoname = it.name
def jsoname1 = jsoname.reverse().take(9).reverse()
list1.add(jsoname1.substring(1,4))
String listAsString = "[\'${list1.join("', '")}\']"
println "return"+listAsString
}
}
directory.eachFileRecurse(findFilenameClosure)
To:
directory.eachFileRecurse {
if (filePattern.matcher(it.name).find()) {
def jsoname = it.name
def jsoname1 = jsoname.reverse().take(9).reverse()
list1.add(jsoname1.substring(1,4))
}
}
return list1

how to get url of an object in cherrypy?

I am new to CherryPy. I am using the default dispatcher, with a URL structure similar to this:
root = Root()
root.page1 = Page1()
root.page1.apple = Apple()
root.page2 = Page2()
root.page2.orange = Orange()
Orange renders a template, in which I need a link to Apple. I could just hardcode /page1/apple/. But how can I get the URL of Apple in a DRY manner?
Can this be done with the default dispatcher in CherryPy, or is it only possible with the Routes dispatcher?
(I am coming from the Django world, where one would use reverse() for this purpose.)
You can access to the mounted apps through
cherrypy.tree.apps[mount_point].root
root is always the mounted instance to the mount point. So a reverse function would look like:
def reverse(cls):
# get link to a class type
for app_url in cherrypy.tree.apps.keys():
if isinstance(cherrypy.tree.apps[app_url].root, cls):
# NOTE: it will return with the first mount point of this class
return app_url
Please find a sample code below that uses your classes. http://localhost:8080/page4/orange/ prints out { Orange and the link to apple: : "/page3/apple" }
import cherrypy
link_to_apple_global = ''
class Orange(object):
def __init__(self):
pass
#cherrypy.expose
#cherrypy.tools.json_out()
def index(self):
return {"Orange and the link to apple: ": link_to_apple_global}
class Page2(object):
def __init__(self):
pass
#cherrypy.expose
def index(self):
return "Page2"
class Apple(object):
def __init__(self):
pass
#cherrypy.expose
def index(self):
return "Apple"
class Page1(object):
def __init__(self):
pass
#cherrypy.expose
def index(self):
return "Page1"
class Root(object):
def __init__(self):
pass
#cherrypy.expose
def index(self):
return "Root"
def reverse(cls):
#return cherrypy.tree.apps.keys()
#return dir(cherrypy.tree.apps[''].root)
#return dir(cherrypy.tree.apps['/page3/apple'].root)
# get link to apple
for app_url in cherrypy.tree.apps.keys():
if isinstance(cherrypy.tree.apps[app_url].root, cls):
# NOTE: it will return with the first instance
return app_url
root = Root()
root.page1 = Page1()
root.page1.apple = Apple()
root.page2 = Page2()
root.page2.orange = Orange()
cherrypy.tree.mount(root, '/')
# if you do not specify the mount points you can find the objects
# in cherrypy.tree.apps[''].root...
cherrypy.tree.mount(root.page1, '/page4')
cherrypy.tree.mount(root.page2, '/page3')
cherrypy.tree.mount(root.page2.orange, '/page4/orange')
cherrypy.tree.mount(root.page1.apple, '/page3/apple')
link_to_apple_global = reverse(Apple)
cherrypy.engine.start()
cherrypy.engine.block()

Django Admin: Many-to-Many listbox doesn't show up with a through parameter

I have the following models:
class Message(models.Model):
date = models.DateTimeField()
user = models.ForeignKey(User)
thread = models.ForeignKey('self', blank=True, null=True)
...
class Forum(models.Model):
name = models.CharField(max_length=24)
messages = models.ManyToManyField(Message, through="Message_forum", blank=True, null=True)
...
class Message_forum(models.Model):
message = models.ForeignKey(Message)
forum = models.ForeignKey(Forum)
status = models.IntegerField()
position = models.IntegerField(blank=True, null=True)
tags = models.ManyToManyField(Tag, blank=True, null=True)
In the admin site, when I go to add/change a forum, I don't see the messages listbox as you'd expect. However, it shows up if I remove the 'through' parameter in the ManyToManyField declaration. What's up with that? I've registered all three models (plus Tag) to the admin site in admin.py.
TIA
Documentation says:
When you specify an intermediary model using the through argument to a
ManyToManyField, the admin will not display a widget by default.
But it's probably possible to display M2M fields in the admin change view even if the through attribute is defined.
class ForumAdminForm(forms.ModelForm):
mm = forms.ModelMultipleChoiceField(
queryset=models.Message.objects.all(),
widget=FilteredSelectMultiple(_('ss'), False, attrs={'rows':'10'}))
def __init__(self, *args, **kwargs):
if 'instance' in kwargs:
initial = kwargs.setdefault('initial', {})
initial['mm'] = [t.service.pk for t in kwargs['instance'].message_forum_set.all()]
forms.ModelForm.__init__(self, *args, **kwargs)
def save(self, commit=True):
instance = forms.ModelForm.save(self, commit)
old_save_m2m = self.save_m2m
def save_m2m():
old_save_m2m()
messages = [s for s in self.cleaned_data['ss']]
for mf in instance.message_forum_set.all():
if mf.service not in messages:
mf.delete()
else:
messages.remove(mf.service)
for message in messages:
Message_forum.objects.create(message=message, forum=instance)
self.save_m2m = save_m2m
return instance
class Meta:
model = models.Forum
class ForumAdmin(admin.ModelAdmin):
form = ForumAdminForm
Take a look at the official documentation:
I learned a lot from #Fedor's answer, but some comments and cleanup may be still beneficial.
class ForumAdminForm(forms.ModelForm):
messages = forms.ModelMultipleChoiceField(
queryset=Message.objects.all(),
widget=FilteredSelectMultiple('Message', False))
# Technically, you don't need to manually set initial here for ForumAdminForm
# However, you NEED to do the following for MessageAdminForm
def __init__(self, *args, **kwargs):
if 'instance' in kwargs:
# a record is being changed. building initial
initial = kwargs.setdefault('initial', {})
initial['messages'] = [t.message.pk for t in kwargs['instance'].message_forum_set.all()]
super(ForumAdminForm, self).__init__(*args, **kwargs)
def save(self, commit=True):
if not self.is_valid():
raise HttpResponseForbidden
instance = super(ForumAdminForm, self).save(self, commit)
def save_m2m_with_through():
messages = [t for t in self.cleaned_data['messages']
old_memberships = instance.message_forum_set.all()
for old in old_memberships:
if old.message not in messages:
# and old membership is cleaned by the user
old.delete()
for message in [x for x in messages not in map(lambda x: x.message, old_memberships)]:
membership = Member_forum(message=messsage, forum=instance)
# You may have to initialize status, position and tag for your need
membership.save()
if commit:
save_m2m_with_through()
else:
self.save_m2m = save_m2m_with_through
return instance
class Meta:
model = Forum
fields = {'name', 'messages')
There's one caveat: if you have another many-to-many relationship in the models (that is without through), super(ForumAdminForm, self).save(self, commit) will set self.save_m2m in case commit is False. However, calling this would cause an error, because this function also tries to save the many-to-many with through as well. You may need to save all other many-to-many relationship manually, or catch the exception, or else.
Django admin nicely support many-to-many intermediary models that using the through argument .
For example you have these Person and Group models with intermediate Membership model:
models.py
from django.db import models
class Person(models.Model):
name = models.CharField(max_length=128)
class Group(models.Model):
name = models.CharField(max_length=128)
members = models.ManyToManyField(Person, through='Membership')
class Membership(models.Model):
person = models.ForeignKey(Person, on_delete=models.CASCADE)
group = models.ForeignKey(Group, on_delete=models.CASCADE)
date_joined = models.DateField()
invite_reason = models.CharField(max_length=64)
Now in admin.py file ,
Define an inline class for the intermediate Membership model:
#admin.register(Membership)
class MembershipInline(admin.TabularInline):
model = Membership
extra = 1
And use them in admin views of models:
#admin.register(Person)
class PersonAdmin(admin.ModelAdmin):
inlines = (MembershipInline,)
#admin.register(Group)
class GroupAdmin(admin.ModelAdmin):
inlines = (MembershipInline,)
More info in official docs:
Models,
Admin

Resources