Coder Social home page Coder Social logo

django-salesforce / django-salesforce Goto Github PK

View Code? Open in Web Editor NEW
320.0 18.0 81.0 1.81 MB

Salesforce integration for Django's ORM using the SF REST API.

Home Page: https://pypi.python.org/pypi/django-salesforce

License: MIT License

Shell 1.55% Python 98.24% HTML 0.21%
django salesforce python

django-salesforce's Introduction

django-salesforce

This library allows you to load, edit and query the objects in any Salesforce instance using Django models. The integration is fairly complete, and generally seamless for most uses. It works by integrating with the Django ORM, allowing access to the objects in your SFDC instance (Salesforce .com) as if they were in a traditional database.

Python 3.8 to 3.12, Django 2.1 to 5.0. (Tested also with Python 3.13b1)

Use with Django 5.0 or 4.2(LTS) currently requires an enteprise license key DJSF_LICENSE_KEY until August 2024 unless you accept the AGPL license and install our otherwise identical package "django-salesforce-agpl" instead. The license keys are available to sponsors. Both versions will be unlocked automatically in Django-salesforce 5.1 when a key will be required for Django 5.1. Use with pre-release Django versions is free.

Quick Start

Install, configure a Salesforce connection, create a Salesforce model and run.

  1. Install django-salesforce: pip install django-salesforce

  2. Add a Salesforce connection to your DATABASES setting:

    'salesforce': {
        'ENGINE': 'salesforce.backend',
        'CONSUMER_KEY': '',                # 'client_id'   in OAuth2 terminology
        'CONSUMER_SECRET': '',             # 'client_secret'
        'USER': '',
        'PASSWORD': '',
        'HOST': 'https://test.salesforce.com',
    }
    

    In the example above, all fields should be populated as follows:

    • CONSUMER_KEY and CONSUMER_SECRET values are for the app used to connect to your Salesforce account. Instructions for how get these are in the Salesforce REST API Documentation. Key and secret can be created on web by:
      • SalesForce Lightning > Setup > Apps > App Manager > New Connected App or by Salesforce Classic > Setup > App Setup > Create > Apps > Connected apps > New.
      • Click "Enable OAuth Settings" in API, then select "Access and manage your data (api)" from available OAuth Scopes.
      • Other red marked fields must be filled, but are not relevant for Django with password authentication. ("Callback URL" should be a safe URL that maybe doesn't exist now, but its path is under your control and doesn't redirect. This would be important if you activate other OAuth mode later.)
      • SalesForce Lightning > Setup > Identity > OAuth and OpenID Connect Settings: Ensure that the "Allow OAuth Username-Password Flows" option is checked. This is important if you use USER/PASSWORD below, and is not the default since Summer '23.
    • USER is the username used to connect.
    • PASSWORD is a concatenation of the user's password and security token. Security token can be set by My Settings / Personal / Reset My Security Token or an new token is received by email after every password change. Security token can be omitted if the local IP address has been whitelisted in Security Controls / Network Access.
    • HOST is https://test.salesforce.com to access a sandbox, or https://login.salesforce.com to access production or your domain https://MyDomainName.my.salesforce.com.
  3. Add salesforce.router.ModelRouter to your DATABASE_ROUTERS setting:

    DATABASE_ROUTERS = [
        "salesforce.router.ModelRouter"
    ]
    

    (This is important for switching between 'salesforce' database for models derived from SalesforceModel and 'default' database for normal models with tables created by migrations, especially for 'django.contrib'.)

  4. Add the salesforce app to your INSTALLED_APPS setting:

    INSTALLED_APPS = {
        "django.contrib.auth",
        "django.contrib.contenttypes",
        ...
        ...
        "salesforce"
    }
    

    (This is necessary for running Salesforce extensions in the command inspectdb --database=salesforce in development, otherwise it is not important.)

  5. Add a setting DJSF_LICENSE_KEY = "Your Name or Company / email //our.signature==" if you need it for Django 5.0 or 4.2.

  6. Verify that everything important is configured correctly by running the command python manage.py check --database=salesforce. You get eventualy the important information about problems related to the previous steps. (clearly without tracebacks)

    That is useful again after you define your database model or if you customize your configuration later and e.g. you configure multiple Salesforce databases.

  7. Define a model that extends salesforce.models.Model (alias SalesforceModel) or export the complete SF schema by python manage.py inspectdb --database=salesforce and simplify it to what you need. The full models file is about 1.5 MB with 500 models and the export takes 2 minutes, but it is a valid models module that works without modification. The output of command inspectdb can be restricted by a list of table_names on the command line, but also ForeignKey fields to omitted models must be pruned to get a valid complete small model.

  8. You're all done! Just use your model like a normal Django model.

    If an error occurs in a request to Salesforce, review the received error message that is exactly copied between braces {...} from the Salesforce response to a Python exception to assist debugging.

    See also: Information on settings up Salesforce connected apps if necessary.

    Note about permissions: Administrator profile are only required to run the full suite of unit tests of this framework; otherwise, as long as the account has rights to read or modify the chosen object, everything should work properly. Introspection by inspectdb doesn't require any additional object permissions.

Optional small features

  • Salesforce alias If you want to use another name for your Salesforce DB connection, define SALESFORCE_DB_ALIAS in your settings file:

    SALESFORCE_DB_ALIAS = 'salesforce'  # default
    
  • Timeout settings To override the default timeout of 15 seconds, define SALESFORCE_QUERY_TIMEOUT in your settings file. It can be one number or better a tuple with a short value for connection timeout and a longer value that includes time for running a query. It never need be longer than 30 seconds:

    SALESFORCE_QUERY_TIMEOUT = (4, 15)  # default (connect timeout, data timeout)
    
  • Automatic stupid admin Create a normal Django admin.py module for your Salesforce models and you can register a minimalistic admin for all omitted Admin classes:

    from salesforce.testrunner.example.universal_admin import register_omitted_classes
    # some admin classes that you wrote manually yet
    # ...
    # end of file
    register_omitted_classes(your_application.models)
    

    This is a rudimentary way to verify that every model works in a sandbox, before hand-writing all admin classes. (Foreign keys to huge tables in the production require a customized admin e.g. with search widgets.)

  • Lazy connect By default, the Django ORM connects to all DBs at startup. To delay SFDC connections until they are actually required, define SF_LAZY_CONNECT=True in your settings file. Be careful when using this setting; since it won't fail during the application boot, it's possible for a bad password to be sent repeatedly, requiring an account reset to fix.

  • Configurable Primary Key Salesforce doesn't allow you to define custom primary keys, so django-salesforce will add them automatically in all cases. You can override only capitalization and use a primary key Id by configuring SF_PK='Id' in your project settings if you prefer Salesforce capitalized field name conventions instead of Django default id.

Advanced usage

  • Multiple Inheritance from Abstract Models - Many Salesforce models use the same sets of fields, but using a single inheritance tree would be too complicated and fragile. Proxy models and mixins are also supported.

  • Testing - By default, tests will be run against the SFDC connection specified in settings.py, which will substantially increase testing time.

    One way to speed this up is to change the SALESFORCE_DB_ALIAS to point to another DB connection (preferably SQLite) during testing using the TEST settings variable. Such simple tests can run without any network access. Django unit tests without SalesforceModel are fast everytimes. Special read only fields (with sf_read_only=...) that are updated only by SFDC e.g. last_modified_date need more parameters to be possible to save them into an alternate database, e.g. by auto_now=True or to play with null=True or default=....

  • Multiple SFDC connections - In most cases, a single connection is all that most apps require, so the default DB connection to use for Salesforce is defined by the SALESFORCE_DB_ALIAS settings variable. This behavior can be also configured by DATABASE_ROUTERS, replacing the use of salesforce.router.ModelRouter.

  • Non SF databases - If SALESFORCE_DB_ALIAS is set to a conventional database, the tables defined by the SF models will be created by migrate. This behavior can be disabled by adding a Meta class with managed=False.

  • Custom Managers - When creating a custom manager for a model, the manager must be a descendant of salesforce.manager.SalesforceManager.

    In most cases, switching DB connections with .using(alias). will be sufficient, but if you need to call a method on your custom manager, you should instead use .db_manager(alias) to select a DB while returning the correct manager, e.g. Contact.objects.db_manager(alias).my_manager(params...)

  • Automatic Field Naming - Most of database columns names can be automatically deduced from Django field name, if no db_column is specified:

    last_name = models.CharField(max_length=80)     # db_column='LastName'
    FirstName = models.CharField(max_length=80)     # db_column='FirstName'
    my_bool = models.BooleanField(custom=True)      # db_column='MyBool__c'
    

    Fields named with an upper case character are never modified, except for the addition of the namespace prefix or the '__c' suffix for custom fields. If you want models with minimal db_column then read Running inspectdb.

  • Query deleted objects - Deleted objects that are in trash bin are not selected by a normal queryset, but if a special method query_all is used then also deleted objects are searched. If a trash bin is supported by the model then a boolean field IsDeleted can be in the model and it is possible to select only deleted objects

    deleted_list = list(Lead.objects.filter(IsDeleted=True).query_all())
    
  • Migrations - Migrations can be used for an alternate test database with SalesforceModel. Then all tables must have Meta options db_table and fields must have option db_column, which is done by inspectdb with default settings. Models exported by introspection inspectdb do not specify the option managed because the default value is True.

    There is probably no reason now to collect old migrations of an application that uses only SalesforceModel if they are related to data stored only in Salesforce. Such old migrations can be easily deleted and a new initial migration can be created again if it would be necessary for offline tests if that migrations directory seems big and obsoleted.

  • Exceptions - Custom exceptions instead of standard Django database exceptions are raised by Django-Salesforce to get more useful information. General exceptions are SalesforceError or a more general custom DatabaseError. They can be imported from salesforce.dbapi.exceptions if database errors should be handled specifically in your app.

Foreign Key Support

Foreign key relationships should work as expected, especially relationships from child to parents are well supported in querysets, but mapping Salesforce SOQL to a purely-relational mapper is a leaky abstraction and some knowledge about limitations of SOQL is useful. Some rejected queries should be usually rewritten to two simpler queries. For the gory details, see Foreign Key Support on the Django-Salesforce wiki.

Introspection and special attributes of fields

Some Salesforce fields can not be fully used without special attributes, namely read-only fields and some default values. Further details can be found in Introspection and Special Attributes of Fields

Caveats

The ultimate goal of development of this package is to support reasonable new features of the Salesforce platform and of new Django versions, but for now here are the potential pitfalls and unimplemented operations:

  • Large Objects — Since the entire result set needs to be transferred over HTTP, and since it's common to have extremely high column counts on full object queries, it's assumed that users will create models that are specific to their individual applications' needs. It is especially important if migrations should be created. Migrations on the full models module are really slow. (Models that have been included with this library are very simplified only for example and documentation purposes and for tests.)
  • Inheritance — When using the default router, all models Salesforce must extend salesforce.models.SalesforceModel. The model router checks for this to determine which models to handle through the Salesforce connection.
  • Database Migrationsmigrate will create new tables only in non-SF databases (useful for unit tests); SFDC tables are assumed to already exist with the appropriate permissions. (A very incomplete implementation of migrations in Salesforce has been in a development repository around for two years. I am satisfied for my purposes. Development for better general usability is the main reason why am I trying to get sponsors.)
  • Unsupported methods: Queryset methods union(), difference(), intersection() and distinct() are e.g. not supported because SOQL doesn't support corresponding operators: UNION, EXCEPT, INTERSECT and DISTINCT.

Backwards-incompatible changes

The last most important:

  • v5.0.2: Removed support for Python 3.7 and Django 2.0
  • v4.2: Some new features or versions implemented after June 2023 can require a license key (sponsorship) or to accept the AGPL license. (AGPL is fine for exclusive open source contribution or for education, but impossible if you do not share all your source codes.) Removed support for Python 3.6

django-salesforce's People

Contributors

balsdorf avatar beniwohli avatar chromakey avatar dmytrolitvinov avatar graingert avatar hynekcer avatar jakeatmacllc avatar jamescooke avatar jbaack avatar jonkiparsky avatar lanterno avatar lociii avatar markgarg avatar matiboy avatar mounirmesselmeni avatar naoyak avatar nirkra avatar philchristensen avatar reggied avatar sbussetti avatar snyk-bot avatar tuky avatar tvanesse avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

django-salesforce's Issues

Compile DateField query

Hi again,

When I try to filter my objects through a DateField, the API returns an error :

value of filter criterion for field 'Date_depart__c' must be of type date and should not be enclosed in quotes

The generated query use quotes around my date and SOQL don't like it :
http://www.salesforce.com/us/developer/docs/soql_sosl/index.htm

I tried to improve the SOQL dialect by pacthing salesforce.backend.compiler but I failed (I have no idea how to do that with Django).

Any idea ?

All the best

DeserializationError: Invalid model identifier?

I generated models from inspectdb. I encountered the following error when I tried to query one instance from Account model:

In [1]: from account.models import Account

In [3]: aa = Account.objects.filter(name='CigarFlowers').all()

In [4]: aa
Out[4]: ---------------------------------------------------------------------------
DeserializationError                      Traceback (most recent call last)
<ipython-input-4-d404401c8c64> in <module>()
----> 1 aa

/usr/lib/python2.7/dist-packages/IPython/core/displayhook.pyc in __call__(self, result)
    245             self.start_displayhook()
    246             self.write_output_prompt()
--> 247             format_dict, md_dict = self.compute_format_data(result)
    248             self.write_format_data(format_dict, md_dict)
    249             self.update_user_ns(result)

/usr/lib/python2.7/dist-packages/IPython/core/displayhook.pyc in compute_format_data(self, result)
    155 
    156         """
--> 157         return self.shell.display_formatter.format(result)
    158 
    159     def write_format_data(self, format_dict, md_dict=None):

/usr/lib/python2.7/dist-packages/IPython/core/formatters.pyc in format(self, obj, include, exclude)
    150             md = None
    151             try:
--> 152                 data = formatter(obj)
    153             except:
    154                 # FIXME: log the exception

/usr/lib/python2.7/dist-packages/IPython/core/formatters.pyc in __call__(self, obj)
    479                 type_pprinters=self.type_printers,
    480                 deferred_pprinters=self.deferred_printers)
--> 481             printer.pretty(obj)
    482             printer.flush()
    483             return stream.getvalue()

/usr/lib/python2.7/dist-packages/IPython/lib/pretty.pyc in pretty(self, obj)
    360                             if callable(meth):
    361                                 return meth(obj, self, cycle)
--> 362             return _default_pprint(obj, self, cycle)
    363         finally:
    364             self.end_group()

/usr/lib/python2.7/dist-packages/IPython/lib/pretty.pyc in _default_pprint(obj, p, cycle)
    480     if getattr(klass, '__repr__', None) not in _baseclass_reprs:
    481         # A user-provided repr.
--> 482         p.text(repr(obj))
    483         return
    484     p.begin_group(1, '<')

/usr/local/lib/python2.7/dist-packages/django/db/models/query.pyc in __repr__(self)
     69 
     70     def __repr__(self):
---> 71         data = list(self[:REPR_OUTPUT_SIZE + 1])
     72         if len(data) > REPR_OUTPUT_SIZE:
     73             data[-1] = "...(remaining elements truncated)..."

/usr/local/lib/python2.7/dist-packages/django/db/models/query.pyc in __iter__(self)
     94                - Responsible for turning the rows into model objects.
     95         """
---> 96         self._fetch_all()
     97         return iter(self._result_cache)
     98 

/usr/local/lib/python2.7/dist-packages/django/db/models/query.pyc in _fetch_all(self)
    855     def _fetch_all(self):
    856         if self._result_cache is None:
--> 857             self._result_cache = list(self.iterator())
    858         if self._prefetch_related_lookups and not self._prefetch_done:
    859             self._prefetch_related_objects()

/usr/local/lib/python2.7/dist-packages/salesforce/backend/query.pyc in iterator(self)
    241 
    242                 pfd = prep_for_deserialize
--> 243                 for res in python.Deserializer(pfd(self.model, r, self.db) for r in cursor.results):
    244                         # Store the source database of the object
    245                         res.object._state.db = self.db

/usr/local/lib/python2.7/dist-packages/django/core/serializers/python.pyc in Deserializer(object_list, **options)
     88     for d in object_list:
     89         # Look up the model and starting build a dict of data for it.
---> 90         Model = _get_model(d["model"])
     91         data = {Model._meta.pk.attname: Model._meta.pk.to_python(d.get("pk", None))}
     92         m2m_data = {}

/usr/local/lib/python2.7/dist-packages/django/core/serializers/python.pyc in _get_model(model_identifier)
    151         Model = None
    152     if Model is None:
--> 153         raise base.DeserializationError("Invalid model identifier: '%s'" % model_identifier)
    154     return Model

DeserializationError: Invalid model identifier: 'account.Account'

It might be worth noting model_class.objects.raw() is not supported

I've found that my queries get so hairy that direct SQL is simpler, so I attempted a simple direct raw sql invocation:

EDIT: The error I presented was incorrect: here's the error I am really seeing:

for email in User.objects.raw('Select Id, Email from User'):
     email

Traceback (most recent call last):
  File "<console>", line 1, in <module>
  File
"/opt/python2.7/lib/python2.7/site-packages/django/db/models/query.py",
line 1480, in __iter__
    query = iter(self.query)
  File
"/opt/python2.7/lib/python2.7/site-packages/django/db/models/sql/query.py",
line 69, in __iter__
    self._execute_query()
  File
"/opt/python2.7/lib/python2.7/site-packages/django/db/models/sql/query.py",
line 83, in _execute_query
    self.cursor.execute(self.sql, self.params)
  File "/tmp/support/salesforce/backend/query.py", line 232, in execute
    raise base.DatabaseError("Unsupported query: %s" % self.query)
DatabaseError: Unsupported query: None

Exception when using ForeignKey: hasattr(): attribute name must be string

I was using salesforce api for django it was quite good actually but I was having a foreign key issue.
I had a model named YearlyData as following
class YearlyData(SalesforceModel):
In this model I have a field coach as:
coach = models.ForeignKey(Contact , related_name='yearlydata_coach_set', on_delete=models.DO_NOTHING, db_column='Coach__c', custom=True, blank=True, null=True)

My model works fine if I comment this line but using this line cause this error message
hasattr(): attribute name must be string
it is not cross database issue as well as both models are of salesforce.

application unittest strategy

Our current strategy for automated unit tests in our django project relies on using an in-memory sqlite3 database, replacing the default engine with django.db.backends.sqlite3. This isn't perfect, as it doesn't exactly match postgresql semantics, but it seems to be generally accepted practice.

How do we write unit tests for our application that uses a django-salesforce database?

I imagine we could start by having our test settings use sqlite3 for the salesforce backend as well, or by removing the salesforce ModelRouter from our test settings.

Points to address include:

  1. The salesforce models are not managed. How to have the test runner create tables for them?
  2. According to README's "Foreign Key Support" (and other caveats), there are queries the sqlite3 backend will support that the salesforce backend will not. How to avoid writing tests that will pass in sqlite3 but fail when run in production against salesforce?

I'll probably also want a test suite that actually runs against a real salesforce backend, but due to speed and API limits I don't think I'll want to rely on that approach exclusively.

Impossible to resolve relationship caused by several driving SObject

Hi,

the following code throws me a curious error from SalesForce :

# Get all voyages for the given account
voyages = DemandeDeFinancement.objects.all().filter(Account__rne='0930897V')

Results on :

A driving SObject type has already been set, all other entity types in the FROM clause must be relationships to the initial object. The driving object is Demande_de_financement__c

Any clue?

This library is still amazing :)

Best,
Pierre

New release?

We've got a bunch of new stuff that can go out in a new release, but curious what opinions @hynekcer might have about version numbers. I think we're due for at least 0.5

The commits in this release are d99c1f0...master and include lazy connect, multiple inheritance, testing and authentication improvements, and quite a bit of refactoring.

Also, I'd like to take this opportunity to move the GitHub Org as discussed in #79, so that when I do the release, the PyPI metadata has the new URL.

Incompatibility with Django 1.8

Per Django 1.8 docs:

### Database backend API

The following changes to the database backend API are documented to assist those writing third-party backends in updating their code:

BaseDatabaseXXX classes have been moved to django.db.backends.base. Please import them from the new locations:

I am working on a patch for this. Looks to be a simple fix of switching imports out.

App failure due to SFDC connectivity issues, even for non-SFDC functions

Yesterday I had some problems on my app due to service disruptions on the Salesforce server instance hosting our data, which caused authorization to fail and trigger the LookupError in auth.authenticate. Are there any recommended solutions for handling this exception in order not to disrupt app functionality (aside from calls to Salesforce)? In other words, when SFDC service is disrupted I'd like SF-related features to fail quietly without having an exception take down the app.

convert_lead() endpoint doesn't seem to honor dynamic auth

Should it? From salesforce/utils.py:

settings_dict = settings.DATABASES['salesforce']
## dot dot dot
soap_client.login(settings_dict['USER'], settings_dict['PASSWORD'])

Looks like this method has a totally separate authentication mechanism. Is this because we can't use the same access_token with beatbox/SOAP?

Assertion Error in tests/test_dynamic_auth

Just a small issue here, it looks like there's an assertion in one of the secondary tests that is raising an error when tested on a deployment without a 'salesforce2' database configuration (presumably using django-salesforce also).

Ideally this test would be skipped when there's only one database, and of course, I still need to get the project's Travis build setup to test multiple connections...

$ bash tests/tests.sh
== tests/test_dynamic_auth ==
Traceback (most recent call last):
  File "manage.py", line 18, in <module>
    execute_from_command_line(sys.argv)
  File "/home/travis/virtualenv/python2.7.8/lib/python2.7/site-packages/django/core/management/__init__.py", line 399, in execute_from_command_line
    utility.execute()
  File "/home/travis/virtualenv/python2.7.8/lib/python2.7/site-packages/django/core/management/__init__.py", line 392, in execute
    self.fetch_command(subcommand).run_from_argv(self.argv)
  File "/home/travis/virtualenv/python2.7.8/lib/python2.7/site-packages/django/core/management/__init__.py", line 261, in fetch_command
    commands = get_commands()
  File "/home/travis/virtualenv/python2.7.8/lib/python2.7/site-packages/django/core/management/__init__.py", line 107, in get_commands
    apps = settings.INSTALLED_APPS
  File "/home/travis/virtualenv/python2.7.8/lib/python2.7/site-packages/django/conf/__init__.py", line 54, in __getattr__
    self._setup(name)
  File "/home/travis/virtualenv/python2.7.8/lib/python2.7/site-packages/django/conf/__init__.py", line 49, in _setup
    self._wrapped = Settings(settings_module)
  File "/home/travis/virtualenv/python2.7.8/lib/python2.7/site-packages/django/conf/__init__.py", line 128, in __init__
    mod = importlib.import_module(self.SETTINGS_MODULE)
  File "/home/travis/virtualenv/python2.7.8/lib/python2.7/site-packages/django/utils/importlib.py", line 40, in import_module
    __import__(name)
  File "/home/travis/build/freelancersunion/django-salesforce/tests/test_dynamic_auth/settings.py", line 4, in <module>
    assert 'salesforce2' in DATABASES
AssertionError
== tests/test_lazy_connect ==
Patched socket to IPv4 only
Creating test database for alias 'default'...
Ignoring test database creation for alias 'salesforce'...
attempting authentication to https://nonsense.example.com
attempting authentication to https://login.salesforce.com
successfully authenticated [email protected]
.
----------------------------------------------------------------------
Ran 1 test in 0.405s
OK
Destroying test database for alias 'default'...
No test database to destroy for alias 'salesforce'...
== tests/test_mixin ==
Patched socket to IPv4 only
attempting authentication to https://login.salesforce.com
successfully authenticated [email protected]
Creating test database for alias 'default'...
Ignoring test database creation for alias 'salesforce'...
.
----------------------------------------------------------------------
Ran 1 test in 1.381s
OK
Destroying test database for alias 'default'...
No test database to destroy for alias 'salesforce'...
The command "bash tests/tests.sh" exited with 1.

INVALID_OPERATION_WITH_EXPIRED_PASSWORD

Since I set SESSION_COOKIE_DOMAIN to something like .my-domain.org, I am receiving the following error (which is blocking):

SalesforceError: {u'message': u'The users password has expired, you must call SetPassword before attempting any other API operations', u'errorCode': u'INVALID_OPERATION_WITH_EXPIRED_PASSWORD'}

Just to be clear, I am in a situation where the back-end lives on one domain that is completely independent from the front-end. The front-end is actually consuming the back-end REST API through AJAX calls and it lives at .my-domain.org (which is different from .my-backend.com). Also django-salesforce is used in the back-end and it may (or may not) help to understand why I'm getting this error message for every API call.

Do you know how to solve the problem?

Documentation needs improvement

The documentation is currently very vague, it basically just tells you to make a model, but do you make a model and define all the fields or do you just create a model with a name matching the table in Salesforce? There's a lot that's just left out.

query construction for "exclude()" is incorrect

logging another issue so I remember to follow up later:

It looks like we simply need up update the query constructor that handles the "exclude()" query function -- currently it builds a default "AND NOT ()", which isn't valid SOQL.

Install packages failed: Error occurred when installing package django-salesforce on Windows 7

running pip install django-salesforce on my windows 7, i get an error.

File "C:\Python27\lib\distutils\util.py", line 124, in convert_path

raise ValueError, "path '%s' cannot be absolute" % pathname

ValueError: path '/Users/pchristensen/Workspace/django-salesforce/.editorconfig' cannot be absolute

Downloading the package from https://pypi.python.org/pypi/django-salesforce/0.3.1, I see in the file django-salesforce-0.3.1\django_salesforce.egg-info\SOURCES.txt that Lines 15 - 65 all start with "/Users/pchristensen/Workspace/".

/Users/pchristensen/Workspace/django-salesforce/.editorconfig
...
/Users/pchristensen/Workspace/django-salesforce/salesforce/tests/test_integration.py

Full Error Details

Install packages failed: Error occurred when installing package django-salesforce.

The following command was executed:

packaging_tool.py install --build-dir C:\Users\UserX\AppData\Local\Temp\pycharm-packaging8918208384109524032.tmp django-salesforce

The error output of the command:

DEPRECATION: --no-install, --no-download, --build, and --no-clean are deprecated. See pypa/pip#906.
Downloading/unpacking django-salesforce
Running setup.py (path:C:\Users\UserX\AppData\Local\Temp\pycharm-packaging8918208384109524032.tmp\django-salesforce\setup.py) egg_info for package django-salesforce
package version: 0.3.1

Installed c:\users\UserX\appdata\local\temp\pycharm-packaging8918208384109524032.tmp\django-salesforce\setuptools_git-1.0-py2.7.egg

Installing collected packages: django-salesforce
Running setup.py install for django-salesforce
package version: 0.3.1

Traceback (most recent call last):
  File "<string>", line 1, in <module>
  File "C:\Users\UserX\AppData\Local\Temp\pycharm-packaging8918208384109524032.tmp\django-salesforce\setup.py", line 90, in <module>
    dist = autosetup()
  File "C:\Users\UserX\AppData\Local\Temp\pycharm-packaging8918208384109524032.tmp\django-salesforce\setup.py", line 86, in autosetup
    url              = "https://github.com/freelancersunion/django-salesforce",
  File "C:\Python27\lib\distutils\core.py", line 152, in setup
    dist.run_commands()
  File "C:\Python27\lib\distutils\dist.py", line 953, in run_commands
    self.run_command(cmd)
  File "C:\Python27\lib\distutils\dist.py", line 972, in run_command
    cmd_obj.run()
  File "C:\Python27\lib\site-packages\setuptools\command\install.py", line 59, in run
    return orig.install.run(self)
  File "C:\Python27\lib\distutils\command\install.py", line 563, in run
    self.run_command('build')
  File "C:\Python27\lib\distutils\cmd.py", line 326, in run_command
    self.distribution.run_command(command)
  File "C:\Python27\lib\distutils\dist.py", line 972, in run_command
    cmd_obj.run()
  File "C:\Python27\lib\distutils\command\build.py", line 127, in run
    self.run_command(cmd_name)
  File "C:\Python27\lib\distutils\cmd.py", line 326, in run_command
    self.distribution.run_command(command)
  File "C:\Python27\lib\distutils\dist.py", line 972, in run_command
    cmd_obj.run()
  File "C:\Python27\lib\site-packages\setuptools\command\build_py.py", line 43, in run
    self.build_package_data()
  File "C:\Python27\lib\site-packages\setuptools\command\build_py.py", line 98, in build_package_data
    for package, src_dir, build_dir, filenames in self.data_files:
  File "C:\Python27\lib\site-packages\setuptools\command\build_py.py", line 55, in __getattr__
    self.data_files = files = self._get_data_files()
  File "C:\Python27\lib\site-packages\setuptools\command\build_py.py", line 67, in _get_data_files
    self.analyze_manifest()
  File "C:\Python27\lib\site-packages\setuptools\command\build_py.py", line 117, in analyze_manifest
    self.run_command('egg_info')
  File "C:\Python27\lib\distutils\cmd.py", line 326, in run_command
    self.distribution.run_command(command)
  File "C:\Python27\lib\distutils\dist.py", line 972, in run_command
    cmd_obj.run()
  File "C:\Python27\lib\site-packages\setuptools\command\egg_info.py", line 164, in run
    self.find_sources()
  File "C:\Python27\lib\site-packages\setuptools\command\egg_info.py", line 187, in find_sources
    mm.run()
  File "C:\Python27\lib\site-packages\setuptools\command\egg_info.py", line 271, in run
    self.add_defaults()
  File "C:\Python27\lib\site-packages\setuptools\command\egg_info.py", line 307, in add_defaults
    self.read_manifest()
  File "C:\Python27\lib\site-packages\setuptools\command\sdist.py", line 244, in read_manifest
    self.filelist.append(line)
  File "C:\Python27\lib\site-packages\setuptools\command\egg_info.py", line 211, in append
    path = convert_path(item)
  File "C:\Python27\lib\distutils\util.py", line 124, in convert_path
    raise ValueError, "path '%s' cannot be absolute" % pathname
ValueError: path '/Users/pchristensen/Workspace/django-salesforce/.editorconfig' cannot be absolute
Complete output from command C:\Users\UserX\.virtualenvs\AppX\Scripts\python.exe -c "import setuptools, tokenize;__file__='C:\\Users\\UserX\\AppData\\Local\\Temp\\pycharm-packaging8918208384109524032.tmp\\django-salesforce\\setup.py';exec(compile(getattr(tokenize, 'open', open)(__file__).read().replace('\r\n', '\n'), __file__, 'exec'))" install --record c:\users\UserX\appdata\local\temp\pip-lohuys-record\install-record.txt --single-version-externally-managed --compile:
package version: 0.3.1
running install
running build
running build_py
creating build
creating build\lib
creating build\lib\salesforce
copying salesforce\admin.py -> build\lib\salesforce
copying salesforce\auth.py -> build\lib\salesforce
copying salesforce\fields.py -> build\lib\salesforce
copying salesforce\models.py -> build\lib\salesforce
copying salesforce\router.py -> build\lib\salesforce
copying salesforce__init__.py -> build\lib\salesforce
creating build\lib\salesforce\backend
copying salesforce\backend\aggregates.py -> build\lib\salesforce\backend
copying salesforce\backend\base.py -> build\lib\salesforce\backend
copying salesforce\backend\client.py -> build\lib\salesforce\backend
copying salesforce\backend\compiler.py -> build\lib\salesforce\backend
copying salesforce\backend\creation.py -> build\lib\salesforce\backend
copying salesforce\backend\driver.py -> build\lib\salesforce\backend
copying salesforce\backend\introspection.py -> build\lib\salesforce\backend
copying salesforce\backend\manager.py -> build\lib\salesforce\backend
copying salesforce\backend\operations.py -> build\lib\salesforce\backend
copying salesforce\backend\query.py -> build\lib\salesforce\backend
copying salesforce\backend\validation.py -> build\lib\salesforce\backend
copying salesforce\backend__init__.py -> build\lib\salesforce\backend
creating build\lib\salesforce\management
copying salesforce\management__init__.py -> build\lib\salesforce\management
creating build\lib\salesforce\testrunner
copying salesforce\testrunner\settings.py -> build\lib\salesforce\testrunner
copying salesforce\testrunner\urls.py -> build\lib\salesforce\testrunner
copying salesforce\testrunner__init__.py -> build\lib\salesforce\testrunner
creating build\lib\salesforce\tests
copying salesforce\tests\test_auth.py -> build\lib\salesforce\tests
copying salesforce\tests\test_browser.py -> build\lib\salesforce\tests
copying salesforce\tests\test_integration.py -> build\lib\salesforce\tests
copying salesforce\tests__init__.py -> build\lib\salesforce\tests
creating build\lib\salesforce\management\commands
copying salesforce\management\commands\inspectdb.py -> build\lib\salesforce\management\commands
copying salesforce\management\commands__init__.py -> build\lib\salesforce\management\commands
creating build\lib\salesforce\testrunner\example
copying salesforce\testrunner\example\admin.py -> build\lib\salesforce\testrunner\example
copying salesforce\testrunner\example\forms.py -> build\lib\salesforce\testrunner\example
copying salesforce\testrunner\example\models.py -> build\lib\salesforce\testrunner\example
copying salesforce\testrunner\example\tests.py -> build\lib\salesforce\testrunner\example
copying salesforce\testrunner\example\urls.py -> build\lib\salesforce\testrunner\example
copying salesforce\testrunner\example\views.py -> build\lib\salesforce\testrunner\example
copying salesforce\testrunner\example__init__.py -> build\lib\salesforce\testrunner\example
running egg_info
writing django_salesforce.egg-info\PKG-INFO
writing top-level names to django_salesforce.egg-info\top_level.txt
writing dependency_links to django_salesforce.egg-info\dependency_links.txt
warning: manifest_maker: standard file '-c' not found
reading manifest file 'django_salesforce.egg-info\SOURCES.txt'
Traceback (most recent call last):

  File "<string>", line 1, in <module>

  File "C:\Users\UserX\AppData\Local\Temp\pycharm-packaging8918208384109524032.tmp\
                               django-salesforce\setup.py", line 90, in <module>
dist = autosetup()
  File "C:\Users\UserX\AppData\...\django-salesforce\setup.py", line 86, in autosetup
url              = "https://github.com/freelancersunion/django-salesforce",
  File "C:\Python27\lib\distutils\core.py", line 152, in setup
dist.run_commands()
  File "C:\Python27\lib\distutils\dist.py", line 953, in run_commands
self.run_command(cmd)
  File "C:\Python27\lib\distutils\dist.py", line 972, in run_command
cmd_obj.run()
  File "C:\Python27\lib\site-packages\setuptools\command\install.py", line 59, in run
return orig.install.run(self)
  File "C:\Python27\lib\distutils\command\install.py", line 563, in run
self.run_command('build')
  File "C:\Python27\lib\distutils\cmd.py", line 326, in run_command
self.distribution.run_command(command)
  File "C:\Python27\lib\distutils\dist.py", line 972, in run_command
cmd_obj.run()
  File "C:\Python27\lib\distutils\command\build.py", line 127, in run
self.run_command(cmd_name)
  File "C:\Python27\lib\distutils\cmd.py", line 326, in run_command
self.distribution.run_command(command)
  File "C:\Python27\lib\distutils\dist.py", line 972, in run_command
cmd_obj.run()
  File "C:\Python27\lib\site-packages\setuptools\command\build_py.py", line 43, in run
self.build_package_data()
  File "C:\Python27\lib\site-packages\setuptools\command\build_py.py", line 98, in build_package_data
for package, src_dir, build_dir, filenames in self.data_files:
  File "C:\Python27\lib\site-packages\setuptools\command\build_py.py", line 55, in **getattr**
self.data_files = files = self._get_data_files()
  File "C:\Python27\lib\site-packages\setuptools\command\build_py.py", line 67, in _get_data_files
self.analyze_manifest()
  File "C:\Python27\lib\site-packages\setuptools\command\build_py.py", line 117, in analyze_manifest
self.run_command('egg_info')
  File "C:\Python27\lib\distutils\cmd.py", line 326, in run_command
self.distribution.run_command(command)
  File "C:\Python27\lib\distutils\dist.py", line 972, in run_command
cmd_obj.run()
  File "C:\Python27\lib\site-packages\setuptools\command\egg_info.py", line 164, in run
self.find_sources()
  File "C:\Python27\lib\site-packages\setuptools\command\egg_info.py", line 187, in find_sources
mm.run()
  File "C:\Python27\lib\site-packages\setuptools\command\egg_info.py", line 271, in run
self.add_defaults()
  File "C:\Python27\lib\site-packages\setuptools\command\egg_info.py", line 307, in add_defaults
self.read_manifest()
  File "C:\Python27\lib\site-packages\setuptools\command\sdist.py", line 244, in read_manifest
self.filelist.append(line)
  File "C:\Python27\lib\site-packages\setuptools\command\egg_info.py", line 211, in append
path = convert_path(item)
  File "C:\Python27\lib\distutils\util.py", line 124, in convert_path
raise ValueError, "path '%s' cannot be absolute" % pathname

ValueError: path '/Users/pchristensen/Workspace/django-salesforce/.editorconfig' cannot be absolute

Setting up a test database

As mentioned in the README, connecting to the SFDC resources can really become time consuming when it comes to repeat unit tests over and over in a project using django-salesforce.

I am trying to set up a testing database (SQLite3) to speed up my development but I am unsure of how to dump the real Salesforce schema into this new database. Could you provide some instructions to work it out? I think these instructions would be nice to have in the README as well.

Python 3 compatibility

I searched about compatibility of django-salesforce requirements. Most of them are still compatible with Python 3. (django, python-oauth2, pytz, simplejson)

I found a package Django REST framework that is also compatible.

It is also theoretically possible to start work on Python 3 support now. (not only strip Python 2.6 soon)

EDIT:
The fix will be soon ready. python-requests is an excelent library that supports also REST very good. (The package "django-rest-framework" is for services not for consumer.) Though I have read somewhere that "python-oauth2" supports Python 3, it was not true.

can't install in virtualenv

Hi, I'm having trouble installing django-salesforce in a virtualenv. I'm seeing this error about setuptools_git but it feels like I shouldn't have to install setuptools_git to get this installed.

ImportError: No module named setuptools_git

When I try pip installing this setuptools-git, I get this message:

error: Error: setup script specifies an absolute path:

Any idea as to why this isn't working?

Here's the full error output from the install of django-salesforce without setuptools-git installed:

% env/bin/pip install django-salesforce
Downloading/unpacking django-salesforce
  Running setup.py egg_info for package django-salesforce
    package version: 0.1.6.2

    Traceback (most recent call last):
      File "<string>", line 16, in <module>
      File "/home/ben/Development/env/build/django-salesforce/setup.py", line 71, in <module>
        dist = autosetup()
      File "/home/ben/Development/env/build/django-salesforce/setup.py", line 67, in autosetup
        url             = "https://github.com/freelancersunion/django-salesforce",
      File "/usr/lib/python2.7/distutils/core.py", line 152, in setup
        dist.run_commands()
      File "/usr/lib/python2.7/distutils/dist.py", line 953, in run_commands
        self.run_command(cmd)
      File "/usr/lib/python2.7/distutils/dist.py", line 972, in run_command
        cmd_obj.run()
      File "<string>", line 14, in replacement_run
      File "/home/ben/Development/env/local/lib/python2.7/site-packages/setuptools/command/egg_info.py", line 259, in find_sources
        mm.run()
      File "/home/ben/Development/env/local/lib/python2.7/site-packages/setuptools/command/egg_info.py", line 325, in run
        self.add_defaults()
      File "/home/ben/Development/env/local/lib/python2.7/site-packages/setuptools/command/egg_info.py", line 364, in add_defaults
        rcfiles = list(walk_revctrl())
      File "/home/ben/Development/env/local/lib/python2.7/site-packages/setuptools/command/sdist.py", line 48, in walk_revctrl
        for item in ep.load()(dirname):
      File "/home/ben/Development/env/local/lib/python2.7/site-packages/pkg_resources.py", line 2031, in load
        entry = __import__(self.module_name, globals(),globals(), ['__name__'])
    ImportError: No module named setuptools_git
    Complete output from command python setup.py egg_info:
    package version: 0.1.6.2

running egg_info

writing pip-egg-info/django_salesforce.egg-info/PKG-INFO

writing top-level names to pip-egg-info/django_salesforce.egg-info/top_level.txt

writing dependency_links to pip-egg-info/django_salesforce.egg-info/dependency_links.txt

writing entry points to pip-egg-info/django_salesforce.egg-info/entry_points.txt

warning: manifest_maker: standard file '-c' not found



Traceback (most recent call last):

  File "<string>", line 16, in <module>

  File "/home/ben/Development/env/build/django-salesforce/setup.py", line 71, in <module>

    dist = autosetup()

  File "/home/ben/Development/env/build/django-salesforce/setup.py", line 67, in autosetup

    url             = "https://github.com/freelancersunion/django-salesforce",

  File "/usr/lib/python2.7/distutils/core.py", line 152, in setup

    dist.run_commands()

  File "/usr/lib/python2.7/distutils/dist.py", line 953, in run_commands

    self.run_command(cmd)

  File "/usr/lib/python2.7/distutils/dist.py", line 972, in run_command

    cmd_obj.run()

  File "<string>", line 14, in replacement_run

  File "/home/ben/Development/env/local/lib/python2.7/site-packages/setuptools/command/egg_info.py", line 259, in find_sources

    mm.run()

  File "/home/ben/Development/env/local/lib/python2.7/site-packages/setuptools/command/egg_info.py", line 325, in run

    self.add_defaults()

  File "/home/ben/Development/env/local/lib/python2.7/site-packages/setuptools/command/egg_info.py", line 364, in add_defaults

    rcfiles = list(walk_revctrl())

  File "/home/ben/Development/env/local/lib/python2.7/site-packages/setuptools/command/sdist.py", line 48, in walk_revctrl

    for item in ep.load()(dirname):

  File "/home/ben/Development/env/local/lib/python2.7/site-packages/pkg_resources.py", line 2031, in load

    entry = __import__(self.module_name, globals(),globals(), ['__name__'])

ImportError: No module named setuptools_git

----------------------------------------
Command python setup.py egg_info failed with error code 1 in /home/ben/Development/env/build/django-salesforce
Storing complete log in /home/ben/.pip/pip.log

Using faked `OneToOneField` in Django 1.7

Hi guys,

As mentioned in #75 , I've been trying to get a faked cross-db relationship, and the following snippet works great:

from sf_interface import models as sf_models

#...
class CompanyUser(models.Model):
    auth_user     = models.OneToOneField(AUTH_USER_MODEL)
    name          = models.CharField(max_length=30)
    SF_CONTACTS   = [(c.pk, str(c.first_name) + " " + \
                            str(c.last_name)  + " - " + \
                            str(c.company_name)) \
                            for c in sf_models.Contact.objects.all()]

    sf_contact_id    = models.CharField(max_length=45, choices=SF_CONTACTS, verbose_name="Contact from Salesforce")

that was before I decided to switch to Django 1.7.

As you surely know, the way apps are imported has changed and you cannot access your models before all the installed apps have been completely loaded. So now the little trick leads us to the error

django.core.exceptions.AppRegistryNotReady: Models aren't loaded yet.

Do you have any idea on how to solve this issue (except "Don't move to 1.7" 😄 )?

document (or remove) IPV4_ONLY

It is very surprising that a django app monkeypatches a function of the standard library's socket module by default.

This is the sort of hack that only gets introduced after some horrible, awful-to-debug deployment, so I am sorry for whatever pain caused you to add this, but the commit message that accompanies it is not clear to me. "backward compatibility switch for networks without IPv6"? (f54704e)

In what circumstances is that necessary? I don't have ipv6 routing on the network I'm on now, and I don't have to patch the socket library to connect to connect to Salesforce or any other servers on ipv4.

Please reconsider having this in the app, or at the very least, mention in the README that importing this app changes getaddrinfo for your entire python process.

cursor() takes no arguments (1 given)

I have problem with cursor method from the compiler class.

I am using Django==1.5.5 with django-salesforce==0.2.0.1. When I try insert object to the SalesForce I am receiving an error:

Traceback:
File "/python2.7/site-packages/django/core/handlers/base.py" in get_response
  115.                         response = callback(request, *callback_args, **callback_kwargs)
File "/python2.7/site-packages/django/views/generic/base.py" in view
  68.             return self.dispatch(request, *args, **kwargs)
File "/python2.7/site-packages/django/views/generic/base.py" in dispatch
  86.         return handler(request, *args, **kwargs)
File "/ptoject-src/products/models.py" in create_from_request
  157.         return self.model.objects.create(**kwargs)
File "/python2.7/site-packages/django/db/models/manager.py" in create
  149.         return self.get_query_set().create(**kwargs)
File "/python2.7/site-packages/django/db/models/query.py" in create
  416.         obj.save(force_insert=True, using=self.db)
File "/ptoject-src/salesforce_api/utils.py" in save
  13.         return super(InsertWebsiteOwnerMixin, self).save(*args, **kwargs)
File "/python2.7/site-packages/django/db/models/base.py" in save
  546.                        force_update=force_update, update_fields=update_fields)
File "/python2.7/site-packages/django/db/models/base.py" in save_base
  591.                                update_fields=update_fields)
File "/python2.7/site-packages/django/db/models/base.py" in save_base
  650.                 result = manager._insert([self], fields=fields, return_id=update_pk, using=using, raw=raw)
File "/python2.7/site-packages/django/db/models/manager.py" in _insert
  215.         return insert_query(self.model, objs, fields, **kwargs)
File "/python2.7/site-packages/django/db/models/query.py" in insert_query
  1675.     return query.get_compiler(using=using).execute_sql(return_id)
File "/python2.7/site-packages/salesforce/backend/compiler.py" in execute_sql
  181.          cursor = self.connection.cursor(query=self.query)

Exception Type: TypeError at /object/status/
Exception Value: cursor() takes no arguments (1 given)

I notice this code should be evaluates only for Django 1.4 (if(DJANGO_14):).

Two minor followup bugs that are side effects of how I was identifying the engine in previous ticket

not really in any way breaking bugs, but just wanted to note them:

  1. when running syncdb, the default connection is used (so typically that's not the sf engine). In that case, is_testing returns a false positive b/c the, for instance, Postgres DatabaseWrapper is not an instance of the Salesforce Wrapper. This is also pretty minor b/c all that happens is that syncdb creates tables in the db that it doesn't need to create.
  2. (known) when running dumpdata, the default manager is used no matter what, and the SF manager (rightly) doesn't support some functions that dumpdata expects to use. Maybe this is just as simple as identifying (somehow) that we're running dumpdata and raising an exception ... or maybe there's a way to disable dumpdata in general... this is a minor issue.

Workarround to use aggregations?

Hi guys,

I would like to use this kind of aggregation:

python
MyModel.objects.aggregate( Avg('duration') )


But it's not working:

SalesforceError at /test/

SELECT Duree_en_jours__c) AS duration__avg FROM Demande_de_financement__c
^
ERROR at Row:1:Column:24
unexpected token: )


I suffered the same error with _Count_, _Max_, _Min_, etc, and other columns.

Did anyone find a way to use aggregations with **django-salesforce**?
Cheers!

Travis CI Build Notes

So, I went ahead and setup a travis build instance for django-salesforce, but it's turning out to be a little trickier than I thought.

The biggest issue is that the 'developer' level account I used appears to be pretty heavily limited API-wise, particularly with respect to running simultaneous builds on different platforms. I've limited that to one combination at this time, Python 2.7 with Django 1.6.2. Other combinations are best tested with the tox test automation tool.

Still, I'm getting failures about API limits being hit even with this single combo, but that may be a result of the heavy testing I was doing earlier. If I'm not able to resolve these issues for the single build within a day or two, I'll turn off the integration so GitHub doesn't give false positives about build results.

Problem with quality of internet connection

Yesterday I observed a strange problem when I tried to run tests while travelling and my notebook was connected by cellural phone. The tested django-salesforce v0.2.9 (that has two retries of packet after socket error)

Insert
One of consequences were sporadic "orphants" object that were not deleted after the test, though no delete failed previously. This was becase the same "twins" objects (except different ID) were created, if the response to the first request failed and the request was repeated even if the first one was successful also. Then only the second one was known and was deleted at the end.

If the object type had a unique key on some columns an error is reported by SF that object with that values exist still. (Only one PricebookEntry can exist at most for any combination of Product+Pricebook. This is the only known such class in salesforce.testrunner.example.models)

Delete
Other problem were errors about object that doesn't exist while it was deleted, becase the packet with delete request was repeated when the answer packet about successful delete was lost.


I did not verified anything at packet level and I did not save any log from this. I do not want to dive into Python implementation of http protocol, ssl etc.

A possible solution for the delete query is easy: to change the severity from SalesforceError to a warning. Warnings can be configured that some type can be only reported or silented or raised as exception, but I doubt that the latter can be iseful.

Inserts are more complicated. I want to ignore it now for long time. Maybe a bug report about some inner package must come later.

upsert ability

Hi, is any functionality regarding upserts planned or is there any easy way to get this to work? As far as I can see only updates and inserts are implemented. Technically, an upsert would have a different url value for the REST API PATCH method:

Instead of
"prefix/sobjects/object_name/id_value" (as in update)
an upsert would take the following url format
"prefix/sobjects/object_name/external_id_name/external_id_value"

We are currently building a synchronization mechanism between a backend system and Salesforce and we now have to do a get for every record that we want to synchronize to find out if we need to update it or insert it. This has a quite an impact on performance of the synchronization method, especially when handling a lot of records.

Switch to soft tabs

We meant to do this months (years?) ago, but put it off because we were under active development. Things are pretty calm now, so I hope to do this ASAP.

Dealing with Lead-Contact conversions

Hi guys,

I am currently struggling with Lead-->Contact conversions. I would like to do it from Django but it seems some fields related to this conversion are not writable. Namely Lead.is_converted and Lead.converted_contact have this sf_read_only=models.NOT_UPDATEABLE set.

Do you know how to deal with this Lead-->Contact conversion with django-salesforce properly?

Currently I have been trying to get it right with the following function

def convert_lead(lead):
    """convert_lead
    Re-implement the `convertLead()` function using the Salesforce
    REST API. At the time this is written, `convertLead()` is only
    available through the SOAP (Apex) API.

    From the official documentation:

    > Convert a Lead into an Account and Contact, as well as (optionally)
    > an Opportunity.

    For the sake of this application, we only need to convert the Lead
    into a Contact.

    This function returns the newly created Contact.
    """
    if not lead.is_converted:
        # Convert the Lead into a Contact
        lead_serial = LeadSerializer(lead)
        contact_serial = ContactSerializer(data=lead_serial.data)

        if contact_serial.is_valid():
            c = contact_serial.object
            c.pk = None  # to avoid using the pk of the Lead instance
            c.save()
            lead.is_converted = True
            lead.status = 'Qualified - converted'
            lead.converted_contact = c
            lead.save()  # this fails (silently) to save the updated lead because of sf_read_only=models.NOT_UPDATEABLE on some fields
            return c
        else:
            # Should not happen
            raise RuntimeError(
                "The provided Lead could not be converted into a Contact.")
    else:
        # This Lead is already converted. Abort the mission.
        raise RuntimeWarning(
            "This Lead has already been converted.")

All the best.

Issue installing Django-Salesforce 0.4 on Windows 7 - 64bit

Dear all,

I struggle to install Django-Salesforce version 0.4 within a virtualenv on WIndows 7 -64 bit.
I saw that Issue #63 solve installing the version 0.4 package django-salesforce on Windows 7. It seems that the version 0.4 did not fix the problem.

Hereunder you'll find the issue as reported on the command line:

PS C:\Users\M.van.Steenberghe\workspace\creo2-django> pip install django-salesforce
Downloading/unpacking django-salesforce
  Running setup.py (path:c:\users\mvan~1.ste\appdata\local\temp\pip_build_m.van.steen
egg_info for package django-salesforce
    package version: 0.4

    warning: no previously-included files found matching '.gitignore'
    warning: no previously-included files found matching 'salesforce\testrunner\local
Requirement already satisfied (use --upgrade to upgrade): django>=1.4.2 in c:\python2
lesforce)
Downloading/unpacking simplejson>=2.5.0 (from django-salesforce)
  Running setup.py (path:c:\users\mvan~1.ste\appdata\local\temp\pip_build_m.van.steen
o for package simplejson

Downloading/unpacking pytz>=2012c (from django-salesforce)
Downloading/unpacking requests>=2.0.0 (from django-salesforce)
Installing collected packages: django-salesforce, simplejson, pytz, requests
  Running setup.py install for django-salesforce
    package version: 0.4

    Traceback (most recent call last):
      File "<string>", line 1, in <module>
      File "c:\users\mvan~1.ste\appdata\local\temp\pip_build_m.van.steenberghe\django
<module>
        dist = autosetup()
      File "c:\users\mvan~1.ste\appdata\local\temp\pip_build_m.van.steenberghe\django
autosetup
        url                              = "https://github.com/freelancersunion/djang
      File "C:\Python27\lib\distutils\core.py", line 151, in setup
        dist.run_commands()
      File "C:\Python27\lib\distutils\dist.py", line 953, in run_commands
        self.run_command(cmd)
      File "C:\Python27\lib\distutils\dist.py", line 972, in run_command
        cmd_obj.run()
      File "C:\Python27\lib\site-packages\setuptools\command\install.py", line 61, in
        return orig.install.run(self)
      File "C:\Python27\lib\distutils\command\install.py", line 563, in run
        self.run_command('build')
      File "C:\Python27\lib\distutils\cmd.py", line 326, in run_command
        self.distribution.run_command(command)
      File "C:\Python27\lib\distutils\dist.py", line 972, in run_command
        cmd_obj.run()
      File "C:\Python27\lib\distutils\command\build.py", line 127, in run
        self.run_command(cmd_name)
      File "C:\Python27\lib\distutils\cmd.py", line 326, in run_command
        self.distribution.run_command(command)
      File "C:\Python27\lib\distutils\dist.py", line 972, in run_command
        cmd_obj.run()
      File "C:\Python27\lib\site-packages\setuptools\command\build_py.py", line 47, i
        self.build_package_data()
      File "C:\Python27\lib\site-packages\setuptools\command\build_py.py", line 103,
        for package, src_dir, build_dir, filenames in self.data_files:
      File "C:\Python27\lib\site-packages\setuptools\command\build_py.py", line 59, i
        self.data_files = files = self._get_data_files()
      File "C:\Python27\lib\site-packages\setuptools\command\build_py.py", line 72, i
        self.analyze_manifest()
      File "C:\Python27\lib\site-packages\setuptools\command\build_py.py", line 123,
        self.run_command('egg_info')
      File "C:\Python27\lib\distutils\cmd.py", line 326, in run_command
        self.distribution.run_command(command)
      File "C:\Python27\lib\distutils\dist.py", line 972, in run_command
        cmd_obj.run()
      File "C:\Python27\lib\site-packages\setuptools\command\egg_info.py", line 168,
        self.find_sources()
      File "C:\Python27\lib\site-packages\setuptools\command\egg_info.py", line 193,
        mm.run()
      File "C:\Python27\lib\site-packages\setuptools\command\egg_info.py", line 277,
        self.add_defaults()
      File "C:\Python27\lib\site-packages\setuptools\command\egg_info.py", line 313,
        self.read_manifest()
      File "C:\Python27\lib\site-packages\setuptools\command\sdist.py", line 251, in
        self.filelist.append(line)
      File "C:\Python27\lib\site-packages\setuptools\command\egg_info.py", line 218,
        path = convert_path(item)
      File "C:\Python27\lib\distutils\util.py", line 124, in convert_path
        raise ValueError, "path '%s' cannot be absolute" % pathname
    ValueError: path '/Users/pchristensen/Workspace/django-salesforce/.editorconfig'
    Complete output from command C:\Python27\python.exe -c "import setuptools, tokeni
\appdata\\local\\temp\\pip_build_m.van.steenberghe\\django-salesforce\\setup.py';exec
open)(__file__).read().replace('\r\n', '\n'), __file__, 'exec'))" install --record c:
p\pip-socsfh-record\install-record.txt --single-version-externally-managed --compile:
    package version: 0.4

running install
running build
running build_py
creating build
creating build\lib
creating build\lib\salesforce
copying salesforce\admin.py -> build\lib\salesforce
copying salesforce\auth.py -> build\lib\salesforce
copying salesforce\fields.py -> build\lib\salesforce
copying salesforce\models.py -> build\lib\salesforce
copying salesforce\router.py -> build\lib\salesforce
copying salesforce\__init__.py -> build\lib\salesforce
creating build\lib\salesforce\backend
copying salesforce\backend\aggregates.py -> build\lib\salesforce\backend
copying salesforce\backend\base.py -> build\lib\salesforce\backend
copying salesforce\backend\client.py -> build\lib\salesforce\backend
copying salesforce\backend\compiler.py -> build\lib\salesforce\backend
copying salesforce\backend\creation.py -> build\lib\salesforce\backend
copying salesforce\backend\driver.py -> build\lib\salesforce\backend
copying salesforce\backend\introspection.py -> build\lib\salesforce\backend
copying salesforce\backend\manager.py -> build\lib\salesforce\backend
copying salesforce\backend\operations.py -> build\lib\salesforce\backend
copying salesforce\backend\query.py -> build\lib\salesforce\backend
copying salesforce\backend\validation.py -> build\lib\salesforce\backend
copying salesforce\backend\__init__.py -> build\lib\salesforce\backend
creating build\lib\salesforce\management
copying salesforce\management\__init__.py -> build\lib\salesforce\management
creating build\lib\salesforce\testrunner
copying salesforce\testrunner\settings.py -> build\lib\salesforce\testrunner
copying salesforce\testrunner\urls.py -> build\lib\salesforce\testrunner
copying salesforce\testrunner\__init__.py -> build\lib\salesforce\testrunner
creating build\lib\salesforce\tests
copying salesforce\tests\test_auth.py -> build\lib\salesforce\tests
copying salesforce\tests\test_browser.py -> build\lib\salesforce\tests
copying salesforce\tests\test_integration.py -> build\lib\salesforce\tests
copying salesforce\tests\test_unit.py -> build\lib\salesforce\tests
copying salesforce\tests\__init__.py -> build\lib\salesforce\tests
creating build\lib\salesforce\management\commands
copying salesforce\management\commands\inspectdb.py -> build\lib\salesforce\managemen
copying salesforce\management\commands\__init__.py -> build\lib\salesforce\management
creating build\lib\salesforce\testrunner\example
copying salesforce\testrunner\example\admin.py -> build\lib\salesforce\testrunner\exa
copying salesforce\testrunner\example\forms.py -> build\lib\salesforce\testrunner\exa
copying salesforce\testrunner\example\models.py -> build\lib\salesforce\testrunner\ex
copying salesforce\testrunner\example\tests.py -> build\lib\salesforce\testrunner\exa
copying salesforce\testrunner\example\urls.py -> build\lib\salesforce\testrunner\exam
copying salesforce\testrunner\example\views.py -> build\lib\salesforce\testrunner\exa
copying salesforce\testrunner\example\__init__.py -> build\lib\salesforce\testrunner\
running egg_info
writing requirements to django_salesforce.egg-info\requires.txt
writing django_salesforce.egg-info\PKG-INFO
writing top-level names to django_salesforce.egg-info\top_level.txt
writing dependency_links to django_salesforce.egg-info\dependency_links.txt
warning: manifest_maker: standard file '-c' not found

reading manifest file 'django_salesforce.egg-info\SOURCES.txt'
Traceback (most recent call last):
  File "<string>", line 1, in <module>
  File "c:\users\mvan~1.ste\appdata\local\temp\pip_build_m.van.steenberghe\django-sal
ule>
    dist = autosetup()
  File "c:\users\mvan~1.ste\appdata\local\temp\pip_build_m.van.steenberghe\django-sal
setup
    url                          = "https://github.com/freelancersunion/django-salesf
  File "C:\Python27\lib\distutils\core.py", line 151, in setup
    dist.run_commands()
  File "C:\Python27\lib\distutils\dist.py", line 953, in run_commands
    self.run_command(cmd)
  File "C:\Python27\lib\distutils\dist.py", line 972, in run_command
    cmd_obj.run()
  File "C:\Python27\lib\site-packages\setuptools\command\install.py", line 61, in run
    return orig.install.run(self)
  File "C:\Python27\lib\distutils\command\install.py", line 563, in run
    self.run_command('build')
  File "C:\Python27\lib\distutils\cmd.py", line 326, in run_command
    self.distribution.run_command(command)
  File "C:\Python27\lib\distutils\dist.py", line 972, in run_command
    cmd_obj.run()
  File "C:\Python27\lib\distutils\command\build.py", line 127, in run
    self.run_command(cmd_name)
  File "C:\Python27\lib\distutils\cmd.py", line 326, in run_command
    self.distribution.run_command(command)
  File "C:\Python27\lib\distutils\dist.py", line 972, in run_command
    cmd_obj.run()
  File "C:\Python27\lib\site-packages\setuptools\command\build_py.py", line 47, in ru
    self.build_package_data()
  File "C:\Python27\lib\site-packages\setuptools\command\build_py.py", line 103, in b
    for package, src_dir, build_dir, filenames in self.data_files:
  File "C:\Python27\lib\site-packages\setuptools\command\build_py.py", line 59, in __
    self.data_files = files = self._get_data_files()
  File "C:\Python27\lib\site-packages\setuptools\command\build_py.py", line 72, in _g
    self.analyze_manifest()
  File "C:\Python27\lib\site-packages\setuptools\command\build_py.py", line 123, in a
    self.run_command('egg_info')
  File "C:\Python27\lib\distutils\cmd.py", line 326, in run_command
    self.distribution.run_command(command)
  File "C:\Python27\lib\distutils\dist.py", line 972, in run_command
    cmd_obj.run()
  File "C:\Python27\lib\site-packages\setuptools\command\egg_info.py", line 168, in r
    self.find_sources()
  File "C:\Python27\lib\site-packages\setuptools\command\egg_info.py", line 193, in f
    mm.run()
  File "C:\Python27\lib\site-packages\setuptools\command\egg_info.py", line 277, in r
    self.add_defaults()
  File "C:\Python27\lib\site-packages\setuptools\command\egg_info.py", line 313, in a
    self.read_manifest()
  File "C:\Python27\lib\site-packages\setuptools\command\sdist.py", line 251, in read
    self.filelist.append(line)
  File "C:\Python27\lib\site-packages\setuptools\command\egg_info.py", line 218, in a
    path = convert_path(item)
  File "C:\Python27\lib\distutils\util.py", line 124, in convert_path
    raise ValueError, "path '%s' cannot be absolute" % pathname
ValueError: path '/Users/pchristensen/Workspace/django-salesforce/.editorconfig' cann

----------------------------------------
Cleaning up...
Command C:\Python27\python.exe -c "import setuptools, tokenize;__file__='c:\\users\\m
p_build_m.van.steenberghe\\django-salesforce\\setup.py';exec(compile(getattr(tokenize
place('\r\n', '\n'), __file__, 'exec'))" install --record c:\users\mvan~1.ste\appdata
ll-record.txt --single-version-externally-managed --compile failed with error code 1
l\temp\pip_build_m.van.steenberghe\django-salesforce
Storing debug log for failure in c:\users\mvan~1.ste\appdata\local\temp\tmpmuspp0

Including a salesforce database in settings.DATABASES can result in slow running unit tests

Running tests which use Django TestCase class can run significantly slower when
a salesforce backend Database is configured in the DATABASES setting. This occurs regardless of wether the salesforce DB router is configured.

When a database using the salesforce backend is defined, TestCase won't use transactions to rollback the database state between each test run. Instead it will resort to manual initialisation and reseting of the database between each test, which is significantly slower than a rollback. This is to be expected since the salesforce DB router can't support transactions.

This behaviour is mentioned in the django docs:
https://docs.djangoproject.com/en/1.6/topics/testing/tools/

"When running on a database that does not support rollback (e.g. MySQL with the
MyISAM storage engine), TestCase falls back to initializing the database by
truncating tables and reloading initial data."

However its might not be clear to a user that this behaviour will occur if any of the configured database backends don't support transactions, even if that test doesn't use that database backend.

https://github.com/django/django/blob/1.6.2/django/test/testcases.py#L843

I can't see a backward compatible workaround for this apart from documenting the situation and not configuring a database using the salesforce backend during tests.

If that makes sense i'm happy to provide a small patch to the readme.

Thanks,
Ant

SalesforceAutoField does not inherit from fields.AutoField

I'm running into an error running tests on a postgres (non-Salesforce) database. The issue is that django doesn't respect SalesforceAutoField.get_internal_type, and generates errors like the following:

IntegrityError: null value in column "Id" violates not-null constraint

Django is not properly viewing the field as an auto-increment primary key. I think this is caused by a bug in Django here, where it checks whether the field is an instance of AutoField instead of checking that f.get_internal_type() == 'AutoField'. The Django source code seems to be inconsistent on this point, sometimes checking get_internal_type, and sometimes checking whether the field is an instance of AutoField. I'm going to submit a patch with test cases to Django, but an easy fix in django-salesforce itself would be to have SalesforceAutofield inherit from fields.AutoField.

How to reproduce

Set your settings file to use a postgres db:

DATABASES = {
    'default': {
        'NAME': 'something',
        'ENGINE': 'django.db.backends.postgresql_psycopg2',
        'USER': 'django',
        'PASSWORD': 'secret',
    },
}

Define a simple model:

from salesforce import models as sf_models
from django.db import models

class Foo(sf_models.Model):
    class Meta:
        managed=True

Run:

$ DJANGO_SETTINGS_MODULE=django_salesforce_example python manage.py syncdb
$ DJANGO_SETTINGS_MODULE=django_salesforce_example python manage.py shell

Then run in the python shell run

>>> from django_salesforce_example.models import *
>>> Foo.objects.create()

This generates the following stack trace:

Traceback (most recent call last):
  File "<console>", line 1, in <module>
  File "/Users/lucaswiman/scratchpads/work/django_salesforce_example/.venv/default/lib/python2.7/site-packages/django/db/models/manager.py", line 157, in create
    return self.get_queryset().create(**kwargs)
  File "/Users/lucaswiman/scratchpads/work/django_salesforce_example/.venv/default/lib/python2.7/site-packages/django/db/models/query.py", line 322, in create
    obj.save(force_insert=True, using=self.db)
  File "/Users/lucaswiman/scratchpads/work/django_salesforce_example/.venv/default/lib/python2.7/site-packages/django/db/models/base.py", line 545, in save
    force_update=force_update, update_fields=update_fields)
  File "/Users/lucaswiman/scratchpads/work/django_salesforce_example/.venv/default/lib/python2.7/site-packages/django/db/models/base.py", line 573, in save_base
    updated = self._save_table(raw, cls, force_insert, force_update, using, update_fields)
  File "/Users/lucaswiman/scratchpads/work/django_salesforce_example/.venv/default/lib/python2.7/site-packages/django/db/models/base.py", line 654, in _save_table
    result = self._do_insert(cls._base_manager, using, fields, update_pk, raw)
  File "/Users/lucaswiman/scratchpads/work/django_salesforce_example/.venv/default/lib/python2.7/site-packages/django/db/models/base.py", line 687, in _do_insert
    using=using, raw=raw)
  File "/Users/lucaswiman/scratchpads/work/django_salesforce_example/.venv/default/lib/python2.7/site-packages/django/db/models/manager.py", line 232, in _insert
    return insert_query(self.model, objs, fields, **kwargs)
  File "/Users/lucaswiman/scratchpads/work/django_salesforce_example/.venv/default/lib/python2.7/site-packages/django/db/models/query.py", line 1514, in insert_query
    return query.get_compiler(using=using).execute_sql(return_id)
  File "/Users/lucaswiman/scratchpads/work/django_salesforce_example/.venv/default/lib/python2.7/site-packages/django/db/models/sql/compiler.py", line 903, in execute_sql
    cursor.execute(sql, params)
  File "/Users/lucaswiman/scratchpads/work/django_salesforce_example/.venv/default/lib/python2.7/site-packages/django/db/backends/util.py", line 69, in execute
    return super(CursorDebugWrapper, self).execute(sql, params)
  File "/Users/lucaswiman/scratchpads/work/django_salesforce_example/.venv/default/lib/python2.7/site-packages/django/db/backends/util.py", line 53, in execute
    return self.cursor.execute(sql, params)
  File "/Users/lucaswiman/scratchpads/work/django_salesforce_example/.venv/default/lib/python2.7/site-packages/django/db/utils.py", line 99, in __exit__
    six.reraise(dj_exc_type, dj_exc_value, traceback)
  File "/Users/lucaswiman/scratchpads/work/django_salesforce_example/.venv/default/lib/python2.7/site-packages/django/db/backends/util.py", line 53, in execute
    return self.cursor.execute(sql, params)
IntegrityError: null value in column "Id" violates not-null constraint
DETAIL:  Failing row contains (null).

Silent omitting to delete record deleted yet

I would like to discuss a new feature, that is inside my beeing prepared series of around 30 commits.

Silent omitting of delete records that have been deleted previously
Example why to do it:

child_object.save()       # Much more commands, baybe a new request
parent_object.delete()
# but Salesforce will delete children e.g. CampaignMember of Campaign
# or they enable/disable it in a new version
child_object.delete()     # Because I want be sure
# This would work with a normal database but not in Salesforce

In a classic SQL it is compiled to DELETE FROM ChildObjects WHERE Id = 'something' and it will raise no exception. Is it acceptable to ignore it silently in django-salesforce? I distinguish two error codes that should be ignored. One of them is that deleted object is in the trash bin yet.

raw query does not seem to support the SOQL "__r" notation

I have some existing SOQL code that is rather complex and was hoping to use in a .raw() call. It appears that the SOQL "__r" notation is not supported:

I have a field on our Asset object, Responsible_SE__c which is of type Lookup(User), that is, a foreign key into the User object. The proper SOQL to see the name of the Responsible_SE__c User would be Responsible_SE__r.Name.

But when I run this code

for asset in Asset.objects.raw(''' Select a.Id, a.Responsible_SE__r.Name FROM Asset a'''):
... asset

I get the following error:

Traceback (most recent call last):
File "", line 1, in
File "/opt/python2.7/lib/python2.7/site-packages/django/db/models/query.py", line 1512, in iter
values = compiler.resolve_columns(values, fields)
File "/u/dale/ds/salesforce/backend/compiler.py", line 25, in resolve_columns
return [row[field.column] for field in fields]
AttributeError: 'NoneType' object has no attribute 'column'

Similarly, if I try
Asset.objects.all().values('serialnumber', 'responsible_se__r__name')

I get the error:

Traceback (most recent call last):
File "", line 1, in
File "/opt/python2.7/lib/python2.7/site-packages/django/db/models/query.py", line 578, in values
return self._clone(klass=ValuesQuerySet, setup=True, _fields=fields)
File "/opt/python2.7/lib/python2.7/site-packages/django/db/models/query.py", line 881, in _clone
c._setup_query()
File "/opt/python2.7/lib/python2.7/site-packages/django/db/models/query.py", line 1005, in _setup_query
self.query.add_fields(self.field_names, True)
File "/opt/python2.7/lib/python2.7/site-packages/django/db/models/sql/query.py", line 1669, in >add_fields
"Choices are: %s" % (name, ", ".join(names)))
FieldError: Cannot resolve keyword 'responsible_se__r__name' into field. Choices are: account, account_owner, etc.

Add 2nd database to Travis test infrastructure

Since the merge of #58, django-salesforce has supported multiple concurrent SF connections, however, the Travis CI infrastructure isn't yet configured to test this.

Creating a new account and adding it to the testrunner config will do the trick.

ManyToMany using OpportunityContactRole

(Is there a mailing list or IRC channel where I should raise things that aren't-necessarily-bugs? This could be a feature request, maybe.)

I'm working with the standard Salesforce Opportunity and Contact objects. They have a many-to-many relationship using OpportunityContactRole, and I'm trying to represent that in Django.

class SalesforceContact(sfmodels.SalesforceModel):
    class Meta:
        db_table = 'Contact'


class SalesforceOpportunity(sfmodels.SalesforceModel):
    class Meta:
        db_table = 'Opportunity'
    contacts = models.ManyToManyField(
        SalesforceContact,
        through='projects.SalesforceOpportunityContactRole',
        related_name='opportunities')


class SalesforceOpportunityContactRole(sfmodels.SalesforceModel):
    class Meta:
        db_table = 'OpportunityContactRole'
        managed = False

    opportunity = models.ForeignKey(SalesforceOpportunity,
                                    db_column='OpportunityId',
                                    on_delete=models.DO_NOTHING,
                                    related_name='contact_roles')
    contact = models.ForeignKey(SalesforceContact,
                                db_column='ContactId',
                                on_delete=models.DO_NOTHING,
                                related_name='opportunity_roles')

    role = models.CharField(db_column='Role', max_length=40,
                            blank=True, null=True)

Individually, these models work for simple queries, and the ForeignKey attributes do behave as expected. But when I tried using the ManyToMany field with opportunity.contacts.all(), we get this:

SELECT Contact.Id, Contact.Email, Contact.FirstName, 
        Contact.LastName, Contact.Salutation, Contact.AccountId
    FROM Contact 
    WHERE OpportunityContactRole.OpportunityId = '006F000000SdskwIAB'

ERROR
Didn't understand relationship 'OpportunityContactRole' in field path. If you are attempting to use a custom relationship, be sure to append the '__r' after the custom relationship name. Please reference your WSDL or the describe call for the appropriate names.

Then I figured that the ManyToMany doesn't work, but maybe a subquery would, so I did SalesforceContact.objects.filter(opportunity_roles=op.contact_roles.all()), but that also fails:

SELECT Contact.Id, Contact.Email, Contact.FirstName, Contact.LastName,
        Contact.Salutation, Contact.AccountId
    FROM Contact 
    WHERE OpportunityContactRole.Id IN (
        SELECT OpportunityContactRole.Id FROM OpportunityContactRole 
        WHERE OpportunityContactRole.OpportunityId = '006F000000SdskwIAB' ) 

Didn't understand relationship 'OpportunityContactRole' in field path. If you are attempting to use a custom relationship, be sure to append the '__r' after the custom relationship name. Please reference your WSDL or the describe call for the appropriate names.

salesforce Manager is not engine-aware

Because of the differences between a salesforce queryset and a standard vanilla one, the SF manager is (correctly) hard coded to use the custom SF QuerySet. So generally speaking, there's no case where you'd want to use a different backend if your object is of SalesForce type. One exception is unit tests, where you'd likely want to switch the salesforce connection over to use sqllite or similar.

** salesforce-django **

SalesforceManager
    ...
    def get_query_set(self): 
        from salesforce.backend.base import DatabaseWrapper 
        if isinstance(connections[self.db], DatabaseWrapper):
            from salesforce.backend import query, compiler
            q = query.SalesforceQuery(self.model, where=compiler.SalesforceWhereNode)
            return query.SalesforceQuerySet(self.model, query=q, using=self.db)
        else:
            return super(SalesforceManager, self).get_query_set() 

** settings **

"DATABASES": {
    ...
    "salesforce" : {
        "ENGINE" : "django.db.backends.sqlite3",
    },
    ...
},

"DATABASE_ROUTERS" : [
remove the salesforce database router from the list, OR also make the router aware of ENGINE so it can fail back to standard behavior if the current connection's DatabaseWrapper is not an instance of the salesforce backend.
],

Implemented READ_ONLY attributes, ForeignKeys, inspectdb

I implemented read only attributes of Salesforce fields like LastModifiedDate and LastModifiedBy.

I also extended the command inspectdb so that almost complete Salesforce API model can be automatically imported to Django. Every table that is retriveable in SF can be used in models.py. The exported file is 100% valid without manual edits. Every model that has any writable fields (without sf_read_only) can be saved. Some extraordinary models are Salesforce MANAGED objects like ApexClass, ApexTrigger that can be naturarely only read by REST API and by Django. (It can be verified by "slow_test.py", but recommended only on sanbox, because it tries to save one object without change in all tables. It writes successfully e.g. to User, UserRole, RecordType.) inspectdb works very good in Django 1.5 and 1.6. It creates approximately 600 kB code. (So manual edits after Django 1.4 are not realistic, but the file can be exported on a new Django and used on an old one.) The exported models can be restricted by 'table_name_filter' option to make it smaller automatically.

The code is in development branch https://github.com/hynekcer/django-salesforce/tree/development

I expect that after week of holidays I rewrite it to a new branch (with a better history, with more compact and better described commits that cover one by one feature without speading it out to many commits that start and fix later many features until it works.) I expect however that the result would be the same if you write no fundamental objections to some feature.

I also wrote the configuration for "tox" (the range from Django 1.3 to Django 1.6 beta, python2.7 and 2.6) and I fixed your code (mostly needed only for Django 1.3 and 1.6) Approximately run only pip install tox and then tox in the directory where is your setup.py and see the test results.


I made minimal changes. I prefer e.g. django.VERSION[:2] >= (1,6) over parse_version(django.get_version()) >= parse_version('1.5.99') if I need a fix for 1.6, but I use my solution only in modules where is no your version comparision.

Migrations in django 1.7 after updating from 1.6

Hi Everyone,
After migrating from django 1.6 to 1.7, I'm not able to run any migrations without commenting out my salesforce models. I get the following traceback:

python manage.py migrate airports --settings=avl.settings.local
Operations to perform:
  Apply all migrations: airports
Running migrations:
  No migrations to apply.
Traceback (most recent call last):
  File "manage.py", line 10, in <module>
    execute_from_command_line(sys.argv)
  File "/srv/.virtualenvs/avl/local/lib/python2.7/site-packages/django/core/management/__init__.py", line 385, in execute_from_command_line
    utility.execute()
  File "/srv/.virtualenvs/avl/local/lib/python2.7/site-packages/django/core/management/__init__.py", line 377, in execute
    self.fetch_command(subcommand).run_from_argv(self.argv)
  File "/srv/.virtualenvs/avl/local/lib/python2.7/site-packages/django/core/management/base.py", line 288, in run_from_argv
    self.execute(*args, **options.__dict__)
  File "/srv/.virtualenvs/avl/local/lib/python2.7/site-packages/django/core/management/base.py", line 338, in execute
    output = self.handle(*args, **options)
  File "/srv/.virtualenvs/avl/local/lib/python2.7/site-packages/django/core/management/commands/migrate.py", line 154, in handle
    ProjectState.from_apps(apps),
  File "/srv/.virtualenvs/avl/local/lib/python2.7/site-packages/django/db/migrations/state.py", line 108, in from_apps
    model_state = ModelState.from_model(model)
  File "/srv/.virtualenvs/avl/local/lib/python2.7/site-packages/django/db/migrations/state.py", line 180, in from_model
    fields.append((name, field_class(*args, **kwargs)))
  File "/srv/.virtualenvs/avl/local/lib/python2.7/site-packages/salesforce/fields.py", line 208, in __init__
    related_object = args[0]
IndexError: tuple index out of range

I have my salesforce model in a separate app called "sfdata". Here's sfdata/models.py:

from salesforce import models
from salesforce.models import SalesforceModel, READ_ONLY

class Account(SalesforceModel):
    Name = models.CharField(max_length=255, verbose_name='Account Name')
    BillingAddress = models.CharField(max_length=255, verbose_name='Billing Address')

class Opportunity(SalesforceModel):
    Name = models.CharField(max_length=120, verbose_name='Name', sf_read_only=READ_ONLY)
    Account = models.ForeignKey(Account, db_column='AccountId')

The only place I utilize this model is in my "contracts" app here:

#contracts/views.py
...
oppy_id = asr_record.sf_oppy_id.split('/')[-1]
oppy_id = oppy_id.split('?')[0]
oppy = Opportunity.objects.get(id=oppy_id)
address_json = oppy.Account.BillingAddress
...

Here's the relevant portions of my settings file:

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.mysql',
        'NAME': 'avl',
        'USER': os.environ['DB_USER'],
        'PASSWORD': os.environ['DB_PASS'],
        'HOST': '127.0.0.1',
        'PORT': '3306',
        'OPTIONS': {
            "init_command": "SET foreign_key_checks = 0;",
        },
    },
    'salesforce': {
        'ENGINE': 'salesforce.backend',
        'CONSUMER_KEY': os.environ['SF_KEY'],
        'CONSUMER_SECRET': os.environ['SF_SECRET'],
        'USER': os.environ['SF_USER'],
        'PASSWORD': os.environ['SF_PASS'],
        'HOST': 'https://login.salesforce.com',
    },
}

DATABASE_ROUTERS = [
    "salesforce.router.ModelRouter"
]

Any thoughts are appreciated. Thanks!

Python 3 Compatibility Notes

So, just a few notes about the current state of Python 3 progress. A huge lift from @hynekcer on #34 and #36 led to our adoption of the requests library and support for Python 3/Django 1.7 in the master branch.

I've been able to personally confirm all functionality on Django 1.6 with Python 2.6, 2.7, and 3.3. My test suite doesn't support Python 3.0 and 3.1, but in Python 3.2 I'm seeing the following:

https://travis-ci.org/freelancersunion/django-salesforce/jobs/21849897

    Traceback (most recent call last):
      File "/home/travis/virtualenv/python3.2/lib/python3.2/site-packages/django/db/utils.py", line 113, in load_backend
        return import_module('%s.base' % backend_name)
      File "/usr/lib/python3.2/importlib/__init__.py", line 124, in import_module
        return _bootstrap._gcd_import(name[level:], package, level)
      File "/usr/lib/python3.2/importlib/_bootstrap.py", line 821, in _gcd_import
        loader.load_module(name)
      File "/usr/lib/python3.2/importlib/_bootstrap.py", line 436, in load_module
        return self._load_module(fullname)
      File "/usr/lib/python3.2/importlib/_bootstrap.py", line 141, in decorated
        return fxn(self, module, *args, **kwargs)
      File "/usr/lib/python3.2/importlib/_bootstrap.py", line 342, in _load_module
        exec(code_object, module.__dict__)
      File "/home/travis/build/freelancersunion/django-salesforce/salesforce/backend/base.py", line 13, in <module>
        import requests
      File "/home/travis/virtualenv/python3.2/lib/python3.2/site-packages/requests/__init__.py", line 58, in <module>
        from . import utils
      File "/home/travis/virtualenv/python3.2/lib/python3.2/site-packages/requests/utils.py", line 24, in <module>
        from . import certs
    ImportError: cannot import name certs

This seems to be more of an infrastructural issue with Travis CI — looking up this string led to references about requests incompatibility with Google App Engine — maybe there's something specific about the Travis builds that causes this. I'm not really concerned about officially supporting anything but 3.3 at this point.

I'm continuing to work on getting testing running for other Django versions.

Dedicated django-salesforce GitHub Org

So, several weeks ago, I changed jobs, and I'm no longer working for Freelancers Union. This doesn't have any real impact on Django-Salesforce from a technical or even practical point of view, but for various reasons, I want to start a dialogue to prepare anyone who's paying close attention for some upcoming changes.

The primary thing I want to address is really just an administrative detail; I'm going to create a new GitHub organization (most likely called 'django-salesforce'), and transfer this repo and all the current user access rules into the new Org.

My departure from Freelancers Union was very positive, merely an evolution of my career, and I eagerly look forward to any other Open Source projects that Freelancers Union might wish to create. But they should be able to manage their GitHub Org freely without having to worry about preserving access rules for projects that are maintained by former employees.

There's a few important points I'd like to make clear:

  • The copyright for django-salesforce will remain with Freelancers Union, and the Union Copyright/URL will continue to appear in headers and documentation.
  • Our MIT licensing means the Union has the right to take development of new code in-house at any point, but in that event the existing open source django-salesforce project would simply continue without the Union's involvement.
  • Contributors will be documented in AUTHORS.md in order of first commit, but accepted submissions will necessarily fall under Freelancers Union copyright, and more importantly, will be subject to the rules and requirements outlined in LICENSE.md

Packaging Issues

The latest versions of django-salesforce require django-1.5, not 1.4
ImportError: six from django.utils.six

And needs to require 'requests' when installing via pip, and this error is really subtle because of the way django.utils catches ImportErrors the missing requests module is hidden behind:
ImproperlyConfigured: 'salesforce.backend' isn't an available database backend

weird errors when "pip install django-salesforce"

My project layout looks something like this:

project_directory
  .git
  env       # virtualenv directory
  src       # python source files
  .gitignore
  fabfile.py
  config.ini
  requirements.txt

When I try to "pip install django-salesforce" (with activated virtualenv), I get errors like this:

 $ pip install django-salesforce
Downloading/unpacking django-salesforce
  Running setup.py egg_info for package django-salesforce
    package version: 0.2.0

Installing collected packages: django-salesforce
  Running setup.py install for django-salesforce
    package version: 0.2.0

    error: Error: setup script specifies an absolute path:

        /home/benjamin/projects/my-awesome-project/fabfile.py

    setup() arguments must *always* be /-separated paths relative to the
    setup.py directory, *never* absolute paths.

    Complete output from command /home/benjamin/projects/my-awesome-project/env/bin/python -c "import setuptools;__file__='/home/benjamin/projects/my-awesome-project/env/build/django-salesforce/setup.py';exec(compile(open(__file__).read().replace('\r\n', '\n'), __file__, 'exec'))" install --record /tmp/pip-Kl93mQ-record/install-record.txt --single-version-externally-managed --install-headers /home/benjamin/projects/my-awesome-project/env/include/site/python2.7:
    package version: 0.2.0

This happens as long as there is anything in project_directory other than env. Even hidden folders and files like .git and .gitignore cause the exception above.

I'm not really grokking your setup.py, so I have no idea what causes this. But I haven't seen this error with any other package.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.