Access related model fields from ModelAdmin actions for exporting to excel - django-modeladmin

I am desperately waiting for someone attention to get my question answered.... please help..
ModelAdmin model has to export to Excel action method.
I need to access related model fields in action method. That means I can not pass any arguments therefore I tried relatedmodel_set but ModelAdmin action method shows memory location and fails when I try to access values through attributes:
<django.db.models.fields.related_descriptors.create_reverse_many_to_one_manager..RelatedManager object at 0x7f8eea904ac0>
model.py
class EnrolStudent(models.Model):
def get_trn_activity(self):
return self.studenttraininactivities_set
class StudentTraininActivities(models.Model):
trainin_activities = models.ForeignKey(EnrolStudent,
on_delete=CASCADE, null=True )
<other fields...>
admin.py
#admin.register(EnrolStudent)
class EnrolAdmin(admin.ModelAdmin):
form = CityInlineForm
inlines = [CohortTraininActivitiesInline]
...
actions = [export_as_txt_action_0120("File NAT00120 data Export"
, fields=['information_no', 'get_trn_activity',
'student_enrol__student_code'])]
I need to access related model fields to export to excel.
I can not pass parameter to get_trn_activity as you have noticed.
Therefore selected rows only data from Django admin change_list page will only need bit of work using its queryset in actions method used in separate actions.py file that I can do!
Please help me with this issue. I am new to Python / Django.
I also tried property decorator in related model then access in a method in main model then call it inside action but same problem with memory address not the direct value and then how to get data of memory location here .... I don't know.
If I can access the related fields then I can do it no issue.
Another question:
I had same situation with model/related model before, but they were connected through OneToOneField relationship and I was able to use dundor to access related model fields but in this case of ForiegnKey relationship I can not see related model in queryset.
In other case this is what I can do easily; here cohortdetails is related model and when I debug I saw it was listed in queryset that was great.
actions = [export_as_txt_action_0080("File NAT00080 txt Export",
fields=['rto_student_code', 'first_name', 'family_name'
,'cohortdetails__highest_school__highestschool_levelcode',
'cohortdetails__cohort_gender'
, 'cohortdetails__student_dob' ])]

Related

How can I create and update pages dynamically in Sulu CMS?

I have the following situation:
A database stores information about houses (address, number of rooms, date built, last selling price, etc.)
This database is being manipulated through an app (let's call that app the "backend house app") that cannot be directly integrated in a Sulu-driven app. I can access the stored data through an API that gives me JSON-representations of House-objects. I can also have the app launch some sort of call to a Sulu-driven app when a house is created, updated or deleted.
The Sulu-driven app (let's call that the "frontend house app") with templates for "house", "room", etc., is connected to a different database on a different server. This Sulu-driven app's website-environment shows house-pages with room-pages where some content is pre-filled through a connection to the "backend house app". Other content only exists on the database of the "frontend house app", like user comments, appraisals of interior design, etc., according to configured aspects of the Sulu-templates.
What I want to achieve, is a way to automate the creation, updating and deletion of "frontend house app"-pages based on activity in the "backend house app".
For instance, when a new house is added in the "backend house app", I want it to notify the "frontend house app" so that the "frontend house app" will automatically create the entire node-tree for the newly added house. Meaning: a "house"-page with the required data filled in, "room"-pages for each room, etc., so that the content manager of the "frontend house app" can see the entire tree of the newly added house in the workspace and can start manipulating content in the already available templates. In addition to automatically creating these pages, I also want to pre-set the rights to update and create, since the content manager of the "frontend house app" must not be able to create new rooms or change the name of the house, for instance.
I did not manage to get it working, I'll just add what I already done to show where I got stuck.
I started out with the following code, in a controller that extends Sulu's own WebsiteController:
$documentManager = $this->get('sulu_document_manager.document_manager');
$nodeManager = $this->get('sulu_document_manager.node_manager');
$parentHousesDocument = $documentManager->find('/cmf/immo/routes/nl/huizen', 'nl');
$newHouseDocument = $documentManager->create('page');
// The backendApi just gives a House object with data from the backend
// In this case we get an existing House with id 1
$house = $backendApi->getHouseWithId(1);
$newHouseDocument->setTitle($house->getName()); // For instance 'Smurfhouse'
$newHouseDocument->setLocale('nl'); // Nl is the only locale we have
$newHouseDocument->setParent($parentHouseDocument); // A default page where all the houses are listed
$newHouseDocument->setStructureType('house'); // Since we have a house.xml template
// I need to grab the structure to fill it with values from the House object
$structure = $newHouseDocument->getStructure();
$structure->bind([
'title' => $house->getName(),
'houseId' => $house->getId(),
]);
$newHouseDocument->setWorkflowStage(WorkflowStage::PUBLISHED); // You would expect this to automatically publish the document, but apparently it doesn't... I took it from a test I reverse-engineered in trying to create a page, I have no clue what it is supposed to change.
$nodeManager->createPath('/cmf/immo/routes/nl/huizen/' . $house->getId());
$documentManager->persist(
$newHouseDocument,
'nl',
[
'path' => '/cmf/immo/contents/huizen/' . Slugifier::slugify($house->getName()), // Assume for argument's sake that the Slugifier just slugifies the name...
'auto_create' => true, // Took this value from a test that creates pages, don't know whether it is necessary
'load_ghost_content' => false, // Idem
]
);
$documentManager->flush();
Now, when I fire the controller action, I first get the exception
Property "url" in structure "house" is required but no value was given.
I tried to fix this by just manually binding the property 'url' with value '/huizen/' . $house->getId() to $structure, at the point where I bind the other values. But this doesn't fix it, as apparently the url value is overwritten somewhere in the persist event chain, and I haven't yet found where.
However, I can, just for testing purposes, manually override the url in the StructureSubscriber that handles the mapping for this particular persist event. If I do this, something gets created in the Sulu-app-database - hurray!
My phpcr_nodes table lists two extra records, one for the RouteDocument referring to /cmf/immo/routes/nl/huizen/1, and one for the PageDocument referring to /cmf/immo/contents/huizen/smurfhouse. Both have the workspace_name column filled with the value default_live. However, as long as there are not also records that are complete duplicates of these two records except with the value default in the workspace_name column, the pages will not appear in the Sulu admin CMS environment. Needless to say, they will also not appear on the public website proper.
Furthermore, when I let the DocumentManager in my controller action try to ->find my newly created document, I get a document of the class UnknownDocument. Hence, I cannot have the DocumentManager go ->publish on it; an Exception ensues. If I visit the pages in the Sulu admin environment, they are hence unpublished; once I publish them there, they can be found by the DocumentManager in the controller action - even if I later unpublish them. They are no longer UnknownDocument, for some reason. However, even if they can be found, I cannot have the DocumentManager go ->unpublish nor ->publish - that just has NO effect on the actual documents.
I was hoping there would be a Sulu cookbook-recipe or another piece of documentation that extensively describes how to create fully published pages dynamically, thus without going through the 'manual labor' of the actual CMS environment, but so far I haven't found one... All help is much appreciated :)
PS: For the purposes of being complete: we're running Sulu on a Windows server environment on PHP 7.1; dbase is PostgreSQL, Sulu being a local forked version of release tag 1.4.7 because I had to make some changes to the way Sulu handles uploaded files to get it to work on a Windows environment.
EDIT: a partial solution for making a new house page if none exists already (not explicitly using the AdminKernel, but should of course be run in a context where the AdminKernel is active):
public function getOrCreateHuisPagina(Huis $huis)
{
$parent = $this->documentManager->find('/cmf/immo/routes/nl/huizen', 'nl'); // This is indeed the route document for the "collector page" of all the houses, but this doesn't seem to give any problems (see below)
try {
$document = $this->documentManager->find('/cmf/immo/routes/nl/huizen/' . $huis->id(), 'nl'); // Here I'm checking whether the page already exists
} catch(DocumentNotFoundException $e) {
$document = $this->setupPublishedPage();
$document->setTitle($huis->naam());
$document->setStructureType('huis_detail');
$document->setResourceSegment('/huizen');
$document->setParent($parent);
$document->getStructure()->bind([
'title' => $huis->naam(), // Not sure if this is required seeing as I already set the title
'huis_id' => $huis->id(),
]);
$this->documentManager->persist(
$document,
'nl',
[
'parent_path' => '/cmf/immo/contents/huizen', // Explicit path to the content document of the parnt
]
);
}
$this->documentManager->publish($document, 'nl');
return $document;
}
First of all I think the following line does not load what you want it to load:
$parentHousesDocument = $documentManager->find('/cmf/immo/routes/nl/huizen', 'nl');
It loads the route instead of the page document, so it should look like the following:
$parentHousesDocument = $documentManager->find('/cmf/immo/contents/nl/huizen', 'nl');
Regarding your error with the URL, instead of overriding the StructureSubscriber you should simple use the setResourceSegment method of the document, which does exactly what you need :-)
And the default_live workspace is wrong, is it possible that you are running these commands on the website kernel? The thing is that the WebsiteKernel has the default_live workspace as default, and therefore writes the content in this workspace. If you run the command with the AdminKernel it should land in the default workspace, and you should be able to copy it into the default_live workspace with the publish method of the DocumentManager.
I hope that helps :-)

Get object from form with errors in play framework 2.0

I am getting an error when I try to extract a user from a form with validation errors.
I have the following route configured in my routes file:
GET /users/:user controllers.UsersController.viewUser(user: models.User)
GET /users/:user/edit controllers.UsersController.editUser(user: models.User)
This is fine at this point, and I can render a link to the user view from my scala templates:
routes.UsersController.viewUser(myUserObject)
My problem is that in my user edition form I need to get myUserObject from a Form[User] object. What I am currently doing is:
routes.UsersController.viewUser(userForm.get)
However, when the userForm has any errors, the get method raises an exception, as shown in the documentation.
The approach I have taken is passing an additional User parameter to the scala view, together with the Form[User] parameter I was passing up to now, I mean,
userEdit.render(user, userForm)
instead of just
userEdit.render(userForm)
However, I would like to know if there is a more suitable solution that does not involve including an additional parameter.
The documentation states that you can prefill a form with existing data:
val filledForm = userForm.fill(User("Bob", 18))
Given the preexisting User, it should be trivial to adapt to your example.

Django Tastypie, Remove Elements From ManyToMany Fields

I am using Tastypie, Django for my project.
To Update a many to many field I have used save_m2m hook.
def save_m2m(self, bundle):
for field_name, field_object in self.fields.items():
if not getattr(field_object, 'is_m2m', False):
continue
if not field_object.attribute:
continue
if field_object.readonly:
continue
related_mngr = getattr(bundle.obj, field_object.attribute)
related_objs = []
print bundle.data[field_name]
for related_bundle in bundle.data[field_name]:
try:
stock = Stock.objects.get(nse_symbol = related_bundle.obj.nse_symbol)
print stock.__dict__
except Stock.DoesNotExist as e:
dataa = {"error_message": e}
raise ImmediateHttpResponse(response=HttpBadRequest(content=json.dumps(dataa), content_type="application/json; charset=UTF-8"))
related_objs.append(stock)
related_mngr.add(*related_objs)
Now I want to remove elements from the same many to many field.
How should I achieve this. Do I have to send a patch request or delete request and how to handle this.
I am begineer in tastypie. I googled it some time and I couldn't find a proper way. Please guide me how to complete this.
Thanks.
I've thought a lot about handing m2m relationships, since most of our app depends on m2m links.
I've settled for the approach of an update method. Pass in the all the references of the relationships you want changed (add and remove), then update the db accordingly. We only pass in the changed values, since if you have a paginated list, you only want to update the items the user has identified. Generally I use a custom hook for this defined in override_urls.
I used to have a separate add and remove method, which worked well until we changed the gui and allowed users simply to change checkboxes. In that approach having an update method was much more useful. You'll have to decide on which method suits your application the best.

Zend Framework / Form Element is rendering as a text box rather than a dropdown box

I have the following in a config.ini file: (Zend_Form_Element)
site_status.name = "site_status"
site_status.type = "select"
site_status.label = "Status"
site_status.options.multiOptions.active.key = "Active"
site_status.options.multiOptions.active.value = "Active"
site_status.options.multiOptions.active.key = "Inactive"
site_status.options.multiOptions.active.value = "Inactive"
As you can see this is supposed to be a dropdown (select) box, however it is being rendered as a standard text box. What am I doing wrong?
--> Edit
Rather than tying the elements to a form, I am trying to tie them to a database: In my code it would look something like this:
[{tablename}] // the table name would represent a section in the ini
{column}.name = "{column_name/form_field_id}";
{column}.type = "{form_element_type}"
{column}.label = "{form_element_label}"
...
From there I would pull in the database table(s) that the form would represent data for (one or more tables as necessary). As far as the reasoning for this approach is that (down the road), I want to define (either by ini or some other storage method), a configuration file that would be a list of fields/elements that belong to a specific form (that a non-programmer type could easily edit), that the 'generic' form class would read, pull in the element info, and create the form on the fly.
I do realize however this poses another problem which I haven't yet figured out, and that is how to use table lookups for select elements (without coding the database retrieval of the lookup into the form, so that a non-user could easily just define it without any programming, purely configuration, but that is a whole other topic not part of my question here. (and I think I have viable ideas/solutions to that part of the problem anyhow) -- extra config entries and a generic routine pretty much.
I hope that clarifies my thought process and reason why I am doing it the way I am in the example above.
I have not yet played with using a Zend_Config to construct an instance of Zend_Form.
But a look at the code suggests that Zend_Form::addElement() doesn't directly take a Zend_Config instance as a param. Rather, it looks like you need pass your Zend_Config instance to the form constructor. It also seems that the config format needs to be a little deeper in order to map config keys to setXXX() calls.
In path/to/config/myForm.ini:
[myForm]
myForm.elements.site_status.name = "site_status"
myForm.elements.site_status.type = "select"
myForm.elements.site_status.label = "Status"
myForm.elements.site_status.options.multiOptions.active.key = "Active"
myForm.elements.site_status.options.multiOptions.active.value = "Active"
myForm.elements.site_status.options.multiOptions.inactive.key = "Inactive"
myForm.elements.site_status.options.multiOptions.inactive.value = "Inactive"
Then instantiating:
$formConfig = new Zend_Config_Ini('path/to/config/myForm.ini', 'myForm');
$form = new Zend_Form($formConfig);
Not tested, but looking at this example:
Using Zend_Form with Zend_Config - Andrew Vayanis
it feels like it should go something like the above.
Update
In view of the comments/feedback from #Aaron, two more approaches.
We could extend Zend_Form, implementing a method called something like addElementByConfig in which we would pass the shallow Zend_Config instance that describes the element itself. In fact, we could even just override addElement(), taking a recursive approach: if the first param is an instance of Zend_Config, then call addElement() using the component data.
If the atomicity and re-usability are the primary benefits we seek in using Zend_Config to describe an element, then perhaps we just make a custom element extending Zend_Form_Element. Then we could use these elements in any forms we wish.

Symfony form gets messy when calling getObject() in form configuration

I have a Strain model that has a belongsTo relationship with a Sample model, i. e. a strain belongs to a sample.
I am configuring a hidden field in the StrainForm configure() method this way:
$defaultId = (int)$this->getObject()->getSample()->getTable()->getDefaultSampleId();
$this->setWidget('sample_id', new sfWidgetFormInputHidden(array('default' => $defaultId)));
Whenever I create a new Strain, the $form->save() fails. The debug toolbar revealed that it tries to save a Sample object first and I do not know why.
However, if I retrieve the default sample ID using the table it works like a charm:
$defaultId = (int)Doctrine_Core::getTable('Sample')->getDefaultSampleId();
$this->setWidget('sample_id', new sfWidgetFormInputHidden(array('default' => $defaultId)));
My question here is what can be happening with the getObject()->getSample()... sequence of methods that causes the StrainForm to think it has to save a Sample object instead of Strain.
I tried to debug with xdebug but I cannot came up with a clear conclusion.
Any thoughts?
Thanks!!
When you call getSample its creating a Sample instance. This is automatically attached to the Strain object, thus when you save you also save the Sample.
An altenrative to calling getSample would be to chain through Strain object to the Sample table since i assume youre only doing this so your not hardcodeing the Sample's name in related form:
// note Sample is the alias not necessarily the Model name
$defaultId = Doctrine_Core::getTable($this->getObject()->getTable()->getRelation('Sample')->getModel())->getDefaultId();
Your solution probably falls over because you can't use getObject() on a new form (as at that stage the object simply doesn't exist).
Edit: Why don't you pass the default Sample in via the options array and then access it from within the form class via $this->getOption('Sample') (if I remember correctly)?