Introduction

Now when you have configured your Redmine object, you can start making requests to Redmine. This document contains an introduction to the most important information about concepts and objects used in Python-Redmine, so it is recommended to read it carefully to understand how everything works.

Operations

Redmine has a concept of resources, i.e. a resource is an entity to which one can apply CRUD operations. Not all resources support every operation, if a resource doesn’t support the requested operation an exception will be thrown. All CRUD operations that you can execute on a resource are provided by a ResourceManager object. This type of object is created automatically for each resource when you request it, e.g:

>>> redmine.project
<redminelib.managers.ResourceManager object for Project resource>
>>> redmine.issue
<redminelib.managers.ResourceManager object for Issue resource>
>>> redmine.user
<redminelib.managers.ResourceManager object for User resource>

Tip

It is recommended to create a ResourceManager object every time on the fly:

# this is good
>>> p1 = redmine.project.get(1)
>>> p2 = redmine.project.get(2)

# this is not so good
>>> project = redmine.project
>>> p1 = project.get(1)
>>> p2 = project.get(2)

create

There are 2 create operations available: create() and new().

create

Creates a new resource with given fields, saves it to the Redmine and returns a newly created Resource object.

>>> project = redmine.project.create(
...     name='Vacation',
...     identifier='vacation',
...     description='foo',
...     homepage='http://foo.bar',
...     is_public=True,
...     parent_id=345,
...     inherit_members=True,
...     custom_fields=[{'id': 1, 'value': 'foo'}, {'id': 2, 'value': 'bar'}]
... )
>>> project
<redminelib.resources.Project #123 "Vacation">

new

Constructs an entirely new Resource object but doesn’t save it to the Redmine until you call save() method manually:

>>> project = redmine.project.new()
>>> project.name = 'Vacation'
>>> project.identifier = 'vacation'
>>> project.description = 'foo'
>>> project.homepage = 'http://foo.bar'
>>> project.is_public = True
>>> project.parent_id = 345
>>> project.inherit_members = True
>>> project.custom_fields = [{'id': 1, 'value': 'foo'}, {'id': 2, 'value': 'bar'}]
>>> project.save()
True

There’s a big difference between creating a resource via create() and new() methods of a ResourceManager. A create() method is the most simple way to create a resource, it just creates a resource with the provided fields, returns a newly created Resource object and that’s it. A new() method doesn’t create a resource immediately, it just constructs an empty Resource object so you can use its attributes to set their values as needed, also when you call a save() method on the resource, at first its pre_create() method will be executed to run pre create tasks if any, then a request to Redmine will be made to actually save the resource and finally a post_create() method will be executed to run post create tasks if any. You can use any method you like, though it is recommended to use a new() method as the most advanced one.

Hint

If there’s no need to process response, one can use a redmine.session with either a return_response=False which doesn’t return any response in a form of Resource object, but still validates it in case there were any errors and raises an exception if needed or a ignore_response=True which totally ignores response even if there were any errors. These attributes can save quite some processing time and if a response isn’t needed one is highly encouraged to use on of them:

with redmine.session(return_response=False):
    redmine.issue.create(project_id=123, subject='Vacation')

with redmine.session(return_response=False):
    issue = redmine.issue.new()
    issue.project_id = 123
    issue.subject = 'Vacation'
    issue.save()

read

There are 3 read operations available: get(), all() and filter(). Each of these methods support different keyword arguments depending on the resource used and method called. You can read more about it in each resource’s documentation.

get

Returns a single Resource object either by integer id or by string identifier:

>>> project = redmine.project.get('vacation')
>>> project
<redminelib.resources.Project #123 "Vacation">

all

Returns a ResourceSet object that contains all the requested Resource objects:

>>> projects = redmine.project.all()
>>> projects
<redminelib.resultsets.ResourceSet object with Project resources>

filter

Returns a ResourceSet object that contains Resource objects filtered by some condition(s):

>>> issues = redmine.issue.filter(project_id='vacation')
>>> issues
<redminelib.resultsets.ResourceSet object with Issue resources>

update

Updates a resource with given fields and saves it to the Redmine.

>>> redmine.project.update(123, name='Work', description='Work tasks')
True

delete

Deletes a resource from Redmine.

>>> redmine.project.delete(1)
True

Warning

Deleted resources can’t be restored. Use this method carefully.

Resource

A Resource object is the most important part of Python-Redmine. Every such object represents a single resource and can be accessed in several ways, for example you can retrieve a resource via get() method of a ResourceManager as discussed earlier:

>>> project = redmine.project.get('vacation')
>>> project.id
123
>>> project.name
'Vacation'

Or create a new resource via create() method of a ResourceManager:

>>> project = redmine.project.create(
...     name='Vacation',
...     identifier='vacation',
...     description='foo',
...     homepage='http://foo.bar',
...     is_public=True,
...     parent_id=345,
...     inherit_members=True,
...     custom_fields=[{'id': 1, 'value': 'foo'}, {'id': 2, 'value': 'bar'}]
... )
>>> project
<redminelib.resources.Project #123 "Vacation">

Or you can construct an entirely new Resource object using new() method of a ResourceManager:

>>> project = redmine.project.new()
>>> project.name = 'Vacation'
>>> project.identifier = 'vacation'
>>> project.description = 'foo'
>>> project.homepage = 'http://foo.bar'
>>> project.is_public = True
>>> project.parent_id = 345
>>> project.inherit_members = True
>>> project.custom_fields = [{'id': 1, 'value': 'foo'}, {'id': 2, 'value': 'bar'}]
>>> project.save()
True

Introspection

Different resources can have different attributes even when they are of the same type. Attributes are constructed dynamically by Python-Redmine when it receives the resource’s data from Redmine. That means that we need to introspect a resource somehow to see what’s inside before we can use it. Fortunately a Resource object provides several ways to introspect itself:

  • dir(). Returns all the attributes resource has as a list.

    >>> dir(redmine.project.get('vacation'))
    ['created_on',
     'description',
     'enabled_modules',
     'id',
     'identifier',
     'issue_categories',
     'issues',
     'memberships',
     'name',
     'news',
     'status',
     'time_entries',
     'trackers',
     'updated_on',
     'versions',
     'wiki_pages']
    
  • list(). Returns all the attributes with their values, which resource has, as a list of tuples.

    >>> list(redmine.project.get('vacation'))
    [('created_on', '2015-11-23T08:18:39Z'),
     ('time_entries', None),
     ('trackers', None),
     ('wiki_pages', None),
     ('status', 1),
     ('description', 'foo'),
     ('news', None),
     ('versions', None),
     ('identifier', 'vacation'),
     ('name', 'Vacation'),
     ('enabled_modules', None),
     ('issues', None),
     ('issue_categories', None),
     ('memberships', None),
     ('id', 123),
     ('updated_on', '2015-11-23T08:20:31Z')]
    
  • repr(). Returns string representation of a resource.

    >>> repr(redmine.project.get('vacation'))
    '<redminelib.resources.Project #123 "Vacation">'
    

Hint

Python has a very handy getattr() function which you can use to access attributes that are not always available, for example Issue resource has is_private attribute which is set to True if issue is private, otherwise this attribute doesn’t exist:

>>> issue = redmine.issue.get(1)
>>> getattr(issue, 'is_private', False)  # prints True if issue is private, False otherwise
False

Export

Added in version 2.0.0.

A Resource object can be exported in one of the formats supported by Redmine using the export() method. A format can be one of the atom, csv, txt, pdf, html etc. You can read more about the supported formats of each resource in its documentation.

>>> issue = redmine.issue.get(123)
>>> issue.export('pdf', savepath='/home/jsmith')
'/home/jsmith/123.pdf'

You can also get access to export url if needed via export_url() method:

>>> issue = redmine.issue.get(123)
>>> issue.export_url('pdf')
'http://demo.redmine.org/issues/123.pdf'

Refresh

In some cases Redmine’s REST API doesn’t provide us with full resource data, fortunately there is a refresh() method for that:

>>> issue = redmine.issue.get(123)
>>> list(issue.project)
[('name', 'FooBar'),
 ('id', 456)]
>>> issue.project.refresh()
>>> list(issue.project)
[('created_on', '2015-12-07T09:19:27Z'),
 ('status', 1),
 ('description', 'A superb project'),
 ('identifier', 'foobar'),
 ('name', 'FooBar'),
 ('homepage', ''),
 ('parent', {'id': 789, 'name': 'Yada'}),
 ('id', 456),
 ('updated_on', '2015-12-07T09:19:27Z')]

Url

Resource object also provides a convenient url attribute which can be used if there is a need to know its url:

>>> issue = redmine.issue.get(123)
>>> issue.url
'http://demo.redmine.org/issues/123'

ResourceSet

A ResourceSet object is another important object type that is used in Python-Redmine. This type of object is constructed automatically by all() or filter() methods of a ResourceManager and is just a collection of Resource objects with a few convenient features. ResourceSet object is lazy, i.e. it doesn’t make any requests to Redmine when it is created and is evaluated only when some of these conditions are met:

  • Iteration. A ResourceSet is iterable and is evaluated when you iterate over it.

    for project in redmine.project.all():
        print(project.name)
    
  • len(). A ResourceSet is evaluated during the len() call and returns the length of itself.

    len(redmine.project.all())
    
  • list(). Force evaluation of a ResourceSet by calling list() on it.

    list(redmine.project.all())
    
  • Index. A ResourceSet is evaluated when some of its resources are requested by index.

    redmine.project.all()[0]  # Returns the first Resource in the ResourceSet
    

Limit/Offset

ResourceSet object supports limit and offset, i.e. if you need to get only some portion of Resource objects, as [offset:limit] or as keyword arguments:

redmine.project.all()[:135]  # Returns only first 135 projects
redmine.project.all(limit=135)  # Returns only first 135 projects
redmine.issue.filter(project_id='vacation')[10:3]  # Returns only 3 issues starting from 10th
redmine.issue.filter(project_id='vacation', offset=10, limit=3)  # Returns only 3 issues starting from 10th

Please note, that keyword arguments have a higher priority, e.g.:

redmine.project.all(limit=10)[:20]  # Returns 10 projects and not 20

Export

Added in version 2.0.0.

A ResourceSet object can be exported in one of the formats supported by Redmine using the export() method. A format can be one of the atom, csv, txt, pdf, html etc. You can read more about the supported formats of each resource in its documentation.

>>> issues = redmine.issue.filter(project_id='vacation', status_id='*')
>>> issues.export('csv', savepath='/home/jsmith', columns='all')
'/home/jsmith/issues.csv'

Methods

ResourceSet object provides several helper methods:

  • get()

    Returns a single resource from the ResourceSet by resource id:

    redmine.project.all().get(30404, None)  # Returns None if a Resource is not found
    
  • filter()

    Changed in version 2.1.0.

    Returns a ResourceSet object filtered on a requested Resource object’s attributes values:

    redmine.project.all().filter(is_public=True, status=1)
    

    It is also possible to follow resource relationships using a double underscore __:

    redmine.issue.all().filter(author__id=1, status__name='New')
    

    Finally it is possible to apply lookups to an attribute using a double underscore __:

    redmine.issue.all().filter(status__name__in=('New', 'Closed'))
    

    If a lookup isn’t defined an exact lookup will be used. The following lookups are available:

    • exact (exact match)

    • in (in a given iterable)

    Due to the fact that Redmine may return resources with different attributes, for example some resources may and some may not have a version attribute defined, one should be very careful with correctly applying filters. For example in a case of typo in one of the attribute names an empty ResourceSet will be returned as there is no way for Python-Redmine to know whether it was a typo or there was just really no resources that could satisfy the filtering conditions.

    Also keep in mind that filtering a ResourceSet is implemented in Python-Redmine and not in the Redmine itself. It is advised to apply all possible filters on the Redmine side first, i.e. using a filter() method of a ResourceManager and then filter everything else using a filter() method of a ResourceSet object. This strategy will ensure that filtering is done in the fastest and most optimized way.

  • update()

    Updates fields of all resources in a resource set with given values and returns an updated ResourceSet object, e.g., the following assigns issues of a project vacation with ids of 30404 and 30405 to the user with id of 547:

    redmine.project.get('vacation').issues.filter((30404, 30405)).update(assigned_to_id=547)
    

    Note

    This method will also call pre_update() and post_update() methods for each Resource object in a ResourceSet.

  • delete()

    Deletes all resources in a ResourceSet, e.g. the following deletes all issues from the vacation project:

    redmine.project.get('vacation').issues.delete()
    

    Note

    This method will also call pre_delete() and post_delete() methods for each Resource object in a ResourceSet.

  • values()

    Returns an iterable of dictionaries rather than resource-instance objects. Each of those dictionaries represents a resource with the keys corresponding to the attribute names of resource objects. This example compares the dictionaries of values() with the normal resource objects:

    >>> list(redmine.issue_status.all(limit=1))
    [<redminelib.resources.IssueStatus #1 "New">]
    >>> list(redmine.issue_status.all(limit=1).values())
    [{'id': 1, 'is_default': True, 'name': 'New'}]
    

    The values() method takes optional positional arguments, *fields, which specify field names to which resource fields should be limited. If you specify fields, each dictionary will contain only the field keys/values for the fields you specify. If you don’t specify the fields, each dictionary will contain a key and value for every field in the resource:

    >>> list(redmine.issue_status.all(limit=1).values())
    [{'id': 1, 'is_default': True, 'name': 'New'}]
    >>> list(redmine.issue_status.all(limit=1).values('id', 'name'))
    [{'id': 1, 'name': 'New'}]
    
  • values_list()

    Added in version 2.0.0.

    Returns an iterable of tuples rather than resource-instance objects. Each of those tuples represents a resource without keys but with ordered values. This example compares the tuples of values_list() with the normal resource objects:

    >>> list(redmine.issue_status.all(limit=2))
    [<redminelib.resources.IssueStatus #1 "New">, <redminelib.resources.IssueStatus #2 "In Progress">]
    >>> list(redmine.issue_status.all(limit=2).values_list())
    [('New', 2), ('In Progress', 3)]
    

    The values_list() method takes optional positional arguments, *fields, which specify field names to which resource fields should be limited. If you specify fields, each tuple will contain only the field values for the fields you specify. If you don’t specify the fields, each tuple will contain a value for every field in the resource:

    >>> list(redmine.issue_status.all(limit=2).values_list())
    [('New', 2), ('In Progress', 3)]
    >>> list(redmine.issue_status.all(limit=2).values_list('id'))
    [(2,), (3,)]
    

    You can also flatten the iterable in case you are interested only in one field:

    >>> list(redmine.issue_status.all(limit=2).values_list('id', flat=True))
    [2, 3]
    

Attributes

ResourceSet object also provides some attributes:

  • limit. What limit value was used to retrieve this resource set:

    >>> projects = redmine.project.all()[100:255]
    >>> projects.limit
    255
    
  • offset. What offset value was used to retrieve this resource set:

    >>> projects = redmine.project.all()[100:255]
    >>> projects.offset
    100
    
  • total_count. How much resources of current resource type there are available in Redmine:

    >>> projects = redmine.project.all()[100:255]
    >>> projects.total_count
    968