How to create hosted feature views including tables

2154
5
Jump to solution
09-23-2019 10:49 AM
JonathanGaudreau1
New Contributor

Hi,

I am trying to create some hosted feature layer views that include the tables as well and I can't seem to find a way to do it. Using the Python API, I am able to either 1) Create a new empty service (as_view), or 2) Create a view from the FeatureCollection, but this leaves away the tables.

This code creates a view with only the feature layers, not the tables:

service = gis.content.get('my_id')
flc= FeatureLayerCollection.fromitem(service)
theVw = flc.manager.create_view("newly_created_view)

This code creates a new service, but it is empty:

gis.content.create_service(name="myTableView",is_view=True)

When I look at what's happening in ArcGIS Online, when I create a view, I see a combination of 3 REST Calls: 1) Create Service, 2) addToDefinition and 3) update. I am trying to replicate this in Python using the add_to_definition fuction, but it does not work, since I would need to run it on a particular layer or table (.e.g. theVw.tables[0].manager.add_to_definition), but if I don't have them in the view, I cannot do anything.

Thanks! Any help is appreciated!

Tags (2)
0 Kudos
1 Solution

Accepted Solutions
EarlMedina
Esri Regular Contributor

Hi Jonathan,

Your problem intrigued me so I decided to take a look at the create_view source code. I remember at least 3 other people seeking this functionality and found it's actually quite simple to implement. The behavior was logged as a defect by a colleague, but if you review the source code I think an Enhancement is more appropriate. The information I present here should not be taken as a solution. Rather, I want it to serve the purpose of bringing awareness to the situation and to provide some insight into how one might troubleshoot these matters. The accepted, supported solution is what I described previously: create the view manually or use the 3 individual methods.

Anyhow, the file we're interested in arcgis/features/managers.py - this contains the create_view method. As you may have guessed, this method basically calls the createService and addToDefinition REST operations. Currently, it is written to only handle layers in the FeatureLayerCollection object and not tables - this is where that limitation comes from.

The important snippet is this one (here I'm just showing one occurrence but it occurs twice):

add_def = {
           "layers" : []
          }
if view_layers is None:
    for lyr in fs.layers:
        add_def['layers'].append(
        {
            "adminLayerInfo" : {
                "viewLayerDefinition" :
            {
                "sourceServiceName" : os.path.basename(os.path.dirname(fs.url)),
                "sourceLayerId" : lyr.manager.properties['id'],
                "sourceLayerFields" :  "*"
            }
            },
            "name" : lyr.manager.properties['name']
         })‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍

Basically, all that needs to be done to enable tables to be created in the view is this:

add_def = {
           "layers" : [],
           "tables" : [] # Add this key
          }
if view_layers is None:
    for lyr in fs.layers:
        add_def['layers'].append(
        {
            "adminLayerInfo" : {
                "viewLayerDefinition" :
            {
                "sourceServiceName" : os.path.basename(os.path.dirname(fs.url)),
                "sourceLayerId" : lyr.manager.properties['id'],
                "sourceLayerFields" :  "*"
            }
            },
            "name" : lyr.manager.properties['name']
         })‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍

    # Add the below loop
    for tbl in fs.tables:
        add_def['tables'].append(
        {
            "adminLayerInfo" : {
                "viewLayerDefinition" :
            {
                "sourceServiceName" : os.path.basename(os.path.dirname(fs.url)),
                "sourceLayerId" : tbl.manager.properties['id'],
                "sourceLayerFields" :  "*"
            }
            },
            "name" : tbl.manager.properties['name']
         })‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍

Thanks for asking about this - I hope the information here can help expedite a more permanent solution.

-Earl

View solution in original post

0 Kudos
5 Replies
EarlMedina
Esri Regular Contributor

Hi Jonathan,

It looks like your situation is described by this defect (logged at version 1.6.1): 

BUG-000122919: When using ArcGIS API for Python to create a view layer of a feature service that contains a layer and a table, the table is not included in the view layer.

  • The workaround is to create the view manually.

It seems you've captured the traffic when doing this manually and can see the which REST operations are involved? There are two add_to_definition methods - I believe you want the one that works on FeatureLayerCollection objects. I've not tested this myself but your approach sounds like it would work.

-Earl

0 Kudos
JonathanGaudreau1
New Contributor

Hi, this is what I did, but the FeatureLayerCollection only works for layers, not tables, right?

0 Kudos
EarlMedina
Esri Regular Contributor

Hi Jonathan, 

I was able to achieve this after using a traffic capture for guidance. I confirm that thebelow operations are needed (I've pointed out the corresponding Python API methods):

REST APIPython API
createServicecreate_service
addToDefinitionadd_to_definition
updateupdate_definition

Relevant import statements:

from arcgis import GIS
from arcgis.features import FeatureLayerCollection

You'll run the below to create a skeleton view - here make sure to set the WKID to match that of the service the view will be based on.

gis.content.create_service(name="test_serv", service_description='', 
                           has_static_data=False, max_record_count=1000, 
                           supported_query_formats='JSON', capabilities='Query', 
                           description='', copyright_text='', wkid=4326, 
                           create_params=None, service_type='featureService', 
                           owner=None, folder=None, item_properties=None, is_view=True)‍‍‍‍‍‍‍

Once the service is created, load it as a FeatureLayerCollection object (let's supporse we call it test_flc).

Next, run add_to_definition, where the input JSON is what you recorded from the traffic capture (I'm not including it here because the JSON is rather large).

test_flc.manager.add_to_definition(input_add_json)

Lastly, run update_definition where again the input JSON is from the traffic capture:

test_flc.manager.update_definition(input_update_json)

Hope this helps!

-Earl

0 Kudos
EarlMedina
Esri Regular Contributor

Hi Jonathan,

Your problem intrigued me so I decided to take a look at the create_view source code. I remember at least 3 other people seeking this functionality and found it's actually quite simple to implement. The behavior was logged as a defect by a colleague, but if you review the source code I think an Enhancement is more appropriate. The information I present here should not be taken as a solution. Rather, I want it to serve the purpose of bringing awareness to the situation and to provide some insight into how one might troubleshoot these matters. The accepted, supported solution is what I described previously: create the view manually or use the 3 individual methods.

Anyhow, the file we're interested in arcgis/features/managers.py - this contains the create_view method. As you may have guessed, this method basically calls the createService and addToDefinition REST operations. Currently, it is written to only handle layers in the FeatureLayerCollection object and not tables - this is where that limitation comes from.

The important snippet is this one (here I'm just showing one occurrence but it occurs twice):

add_def = {
           "layers" : []
          }
if view_layers is None:
    for lyr in fs.layers:
        add_def['layers'].append(
        {
            "adminLayerInfo" : {
                "viewLayerDefinition" :
            {
                "sourceServiceName" : os.path.basename(os.path.dirname(fs.url)),
                "sourceLayerId" : lyr.manager.properties['id'],
                "sourceLayerFields" :  "*"
            }
            },
            "name" : lyr.manager.properties['name']
         })‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍

Basically, all that needs to be done to enable tables to be created in the view is this:

add_def = {
           "layers" : [],
           "tables" : [] # Add this key
          }
if view_layers is None:
    for lyr in fs.layers:
        add_def['layers'].append(
        {
            "adminLayerInfo" : {
                "viewLayerDefinition" :
            {
                "sourceServiceName" : os.path.basename(os.path.dirname(fs.url)),
                "sourceLayerId" : lyr.manager.properties['id'],
                "sourceLayerFields" :  "*"
            }
            },
            "name" : lyr.manager.properties['name']
         })‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍

    # Add the below loop
    for tbl in fs.tables:
        add_def['tables'].append(
        {
            "adminLayerInfo" : {
                "viewLayerDefinition" :
            {
                "sourceServiceName" : os.path.basename(os.path.dirname(fs.url)),
                "sourceLayerId" : tbl.manager.properties['id'],
                "sourceLayerFields" :  "*"
            }
            },
            "name" : tbl.manager.properties['name']
         })‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍

Thanks for asking about this - I hope the information here can help expedite a more permanent solution.

-Earl

0 Kudos
JonathanGaudreau1
New Contributor

Wow, that is amazing! Thanks. I changed the python function on my side and now the result takes everything.

0 Kudos